From a1c6480360560ba73d9d4797ed970103c53ba7a4 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 6 Dec 2016 14:35:56 +0100 Subject: Fix projection matrix for DepthAware QSGRenderNodes Unlike renderUnmergedBatches(), renderRenderNode() did not adjust the projection matrix. Change-Id: Ie8ab0024a5f743ae30fe469f694805f803116613 Reviewed-by: Gunnar Sletta --- src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index 81aa641e03..ae13163ea7 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -2775,8 +2775,13 @@ void Renderer::renderRenderNode(Batch *batch) updateClip(rd->m_clip_list, batch); - RenderNodeState state; QMatrix4x4 pm = projectionMatrix(); + if (m_useDepthBuffer) { + pm(2, 2) = m_zRange; + pm(2, 3) = 1.0f - e->order * m_zRange; + } + + RenderNodeState state; state.m_projectionMatrix = ± state.m_scissorEnabled = m_currentClipType & ScissorClip; state.m_stencilEnabled = m_currentClipType & StencilClip; -- cgit v1.2.3 From c9023c28764e70cd1c6f9cfc3506e6185299548e Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Sat, 3 Dec 2016 21:30:12 +0100 Subject: Add QQuickPathItem and its backend infra The generic backend uses the triangulator from QtGui, but is in fact OpenGL-only for now due to materials. The NVPR backend uses GL_NV_path_rendering on NVIDIA hardware with OpenGL 4.3+ or OpenGL ES 3.1+. The software backend simply uses QPainter. With the generic backend each PathItem is backed by a non-visual root node and 0, 1 or 2 child geometry nodes, depending on the presence of visible stroking and filling. The potentially expensive triangulation happens on updatePolish(), on the gui thread. This is proven to provide much smoother results when compared to doing the geometry generation on the render thread in updatePaintNode(), in particular on power-limited embedded devices. The NVPR backend uses a QSGRenderNode in DepthAware mode so that the batch renderer can continue to rely on the depth buffer and use opaque batches. Due to not relying on slow CPU-side triangulation, this backend uses 5-10 times less CPU, even when properties of the path or its elements are animated. The path itself is specified with the PathView's Path, PathLine, PathArc, PathQuad, etc. types. This allows for consistency with PathView and the 2D Canvas and avoids a naming mess in the API. However, there won't be a 100% symmetry: backends like NVPR will not rely on QPainterPath but process the path elements on their own (as QPainterPath is essentially useless with these APIs), which can lead to differences in the supported path elements. The supported common set is currently Move, Line, Quad, Cubic, Arc. The patch introduces PathMove, which is essentially PathLine but maps to moveTo instead of lineTo. More types may get added later (e.g. NVPR can do a wide variety of optimized rounded rects, but this requires directly specifying a GL_ROUNDED_RECTx_NV command, thus neededing a dedicated Path type on our side too) For filling with gradients only linear gradients are supported at the moment. In addition to the declarative API, a more lightweight, QObject-less JS-callable API should be considered as well for the future. Change-Id: I335ad64b425ee279505d60e3e57ac6841e1cbd24 Reviewed-by: Andy Nichols --- src/quick/items/items.pri | 15 +- src/quick/items/items.qrc | 4 + src/quick/items/qquickitemsmodule.cpp | 9 + src/quick/items/qquickpathitem.cpp | 792 +++++++++++++++++++++ src/quick/items/qquickpathitem_p.h | 281 ++++++++ src/quick/items/qquickpathitem_p_p.h | 177 +++++ src/quick/items/qquickpathitemgenericrenderer.cpp | 510 +++++++++++++ src/quick/items/qquickpathitemgenericrenderer_p.h | 232 ++++++ src/quick/items/qquickpathitemnvprrenderer.cpp | 567 +++++++++++++++ src/quick/items/qquickpathitemnvprrenderer_p.h | 194 +++++ src/quick/items/qquickpathitemsoftwarerenderer.cpp | 234 ++++++ src/quick/items/qquickpathitemsoftwarerenderer_p.h | 127 ++++ src/quick/items/shaders/lineargradient.frag | 9 + src/quick/items/shaders/lineargradient.vert | 15 + src/quick/items/shaders/lineargradient_core.frag | 12 + src/quick/items/shaders/lineargradient_core.vert | 17 + src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp | 4 + src/quick/scenegraph/coreapi/qsgrendernode.cpp | 3 - src/quick/scenegraph/qsgdefaultrendercontext.cpp | 2 +- src/quick/scenegraph/util/qsgtexture.cpp | 24 +- src/quick/scenegraph/util/qsgtexture.h | 3 +- src/quick/scenegraph/util/qsgtexture_p.h | 4 +- src/quick/scenegraph/util/qsgtexturematerial.cpp | 4 +- src/quick/util/qquicknvprfunctions.cpp | 284 ++++++++ src/quick/util/qquicknvprfunctions_p.h | 406 +++++++++++ src/quick/util/qquicknvprfunctions_p_p.h | 70 ++ src/quick/util/qquickpath.cpp | 70 +- src/quick/util/qquickpath_p.h | 10 + src/quick/util/util.pri | 7 + 29 files changed, 4068 insertions(+), 18 deletions(-) create mode 100644 src/quick/items/qquickpathitem.cpp create mode 100644 src/quick/items/qquickpathitem_p.h create mode 100644 src/quick/items/qquickpathitem_p_p.h create mode 100644 src/quick/items/qquickpathitemgenericrenderer.cpp create mode 100644 src/quick/items/qquickpathitemgenericrenderer_p.h create mode 100644 src/quick/items/qquickpathitemnvprrenderer.cpp create mode 100644 src/quick/items/qquickpathitemnvprrenderer_p.h create mode 100644 src/quick/items/qquickpathitemsoftwarerenderer.cpp create mode 100644 src/quick/items/qquickpathitemsoftwarerenderer_p.h create mode 100644 src/quick/items/shaders/lineargradient.frag create mode 100644 src/quick/items/shaders/lineargradient.vert create mode 100644 src/quick/items/shaders/lineargradient_core.frag create mode 100644 src/quick/items/shaders/lineargradient_core.vert create mode 100644 src/quick/util/qquicknvprfunctions.cpp create mode 100644 src/quick/util/qquicknvprfunctions_p.h create mode 100644 src/quick/util/qquicknvprfunctions_p_p.h (limited to 'src') diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri index 0f8061b5ef..511c6f18d8 100644 --- a/src/quick/items/items.pri +++ b/src/quick/items/items.pri @@ -148,9 +148,20 @@ qtConfig(quick-listview) { qtConfig(quick-pathview) { HEADERS += \ $$PWD/qquickpathview_p.h \ - $$PWD/qquickpathview_p_p.h + $$PWD/qquickpathview_p_p.h \ + $$PWD/qquickpathitem_p.h \ + $$PWD/qquickpathitem_p_p.h \ + $$PWD/qquickpathitemgenericrenderer_p.h \ + $$PWD/qquickpathitemsoftwarerenderer_p.h SOURCES += \ - $$PWD/qquickpathview.cpp + $$PWD/qquickpathview.cpp \ + $$PWD/qquickpathitem.cpp \ + $$PWD/qquickpathitemgenericrenderer.cpp \ + $$PWD/qquickpathitemsoftwarerenderer.cpp + qtConfig(opengl) { + HEADERS += $$PWD/qquickpathitemnvprrenderer_p.h + SOURCES += $$PWD/qquickpathitemnvprrenderer.cpp + } } qtConfig(quick-positioners) { diff --git a/src/quick/items/items.qrc b/src/quick/items/items.qrc index 6aaf757c29..da9bf0c828 100644 --- a/src/quick/items/items.qrc +++ b/src/quick/items/items.qrc @@ -8,5 +8,9 @@ shaders/shadereffect_core.vert shaders/shadereffectfallback_core.frag shaders/shadereffectfallback_core.vert + shaders/lineargradient.vert + shaders/lineargradient.frag + shaders/lineargradient_core.vert + shaders/lineargradient_core.frag diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index dbe30fbc83..b0d7f7f8a3 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -70,6 +70,7 @@ #if QT_CONFIG(quick_path) #include #include +#include "qquickpathitem_p.h" #endif #if QT_CONFIG(quick_positioners) #include "qquickpositioners_p.h" @@ -370,6 +371,14 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) #endif qmlRegisterType(uri, 2, 9, "MouseArea"); + +#if QT_CONFIG(quick_path) + qmlRegisterType(uri, 2, 9, "PathMove"); + qmlRegisterType(uri, 2, 9, "PathItem"); + qmlRegisterType(uri, 2, 9, "PathGradientStop"); + qmlRegisterUncreatableType(uri, 2, 9, "PathGradient", QQuickPathGradient::tr("PathGradient is an abstract base class")); + qmlRegisterType(uri, 2, 9, "PathLinearGradient"); +#endif } static void initResources() diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp new file mode 100644 index 0000000000..0d2c7a8bbe --- /dev/null +++ b/src/quick/items/qquickpathitem.cpp @@ -0,0 +1,792 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickpathitem_p.h" +#include "qquickpathitem_p_p.h" +#include "qquickpathitemgenericrenderer_p.h" +#include "qquickpathitemnvprrenderer_p.h" +#include "qquickpathitemsoftwarerenderer_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QQuickPathItemPrivate::QQuickPathItemPrivate() + : rendererType(QQuickPathItem::UnknownRenderer), + renderer(nullptr), + path(nullptr), + dirty(DirtyAll), + strokeColor(Qt::white), + strokeWidth(1), + fillColor(Qt::white), + fillRule(QQuickPathItem::OddEvenFill), + joinStyle(QQuickPathItem::BevelJoin), + miterLimit(2), + capStyle(QQuickPathItem::SquareCap), + strokeStyle(QQuickPathItem::SolidLine), + dashOffset(0), + fillGradient(nullptr) +{ + dashPattern << 4 << 2; // 4 * strokeWidth dash followed by 2 * strokeWidth space +} + +QQuickPathItemPrivate::~QQuickPathItemPrivate() +{ + delete renderer; +} + +/*! + \qmltype PathItem + \instantiates QQuickPathItem + \inqmlmodule QtQuick + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Item + \brief Renders a path + + Renders a path either by generating geometry via QPainterPath and manual + triangulation or by using an extension like \c{GL_NV_path_rendering}. + + This approach is different from rendering shapes via QQuickPaintedItem or + the 2D Canvas because the path never gets rasterized in software. Therefore + it is suitable for creating shapes spreading over larger areas of the + screen, avoiding the performance penalty for texture uploads or framebuffer + blits. + + Nonetheless it is important to be aware of performance implications, in + particular when the application is running on the generic PathItem + implementation due to not having support for accelerated path rendering. + The geometry generation happens entirely on the CPU in this case, and this + is potentially expensive. Changing the set of path elements, changing the + properties of these elements, or changing certain properties of the + PathItem itself all lead to retriangulation on every change. Therefore, + applying animation to such properties can heavily affect performance on + less powerful systems. If animating properties other than stroke and fill + colors is a must, it is recommended to target systems providing + \c{GL_NV_path_rendering} where the cost of path property changes is much + smaller. + + \note The types for specifying path elements are shared between PathView + and PathItem. However, not all PathItem implementations support all path + element types, while some may not make sense for PathView. PathItem's + currently supported subset is: PathMove, PathLine, PathQuad, PathCubic, + PathArc. + + \sa Path, PathMove, PathLine, PathQuad, PathCubic, PathArc +*/ + +QQuickPathItem::QQuickPathItem(QQuickItem *parent) + : QQuickItem(*(new QQuickPathItemPrivate), parent) +{ + setFlag(ItemHasContents); +} + +QQuickPathItem::~QQuickPathItem() +{ +} + +QQuickPathItem::RendererType QQuickPathItem::rendererType() const +{ + Q_D(const QQuickPathItem); + return d->rendererType; +} + +/*! + \qmlproperty Path QtQuick::PathItem::path + This property holds the path to be rendered. + For more information see the \l Path documentation. +*/ +QQuickPath *QQuickPathItem::path() const +{ + Q_D(const QQuickPathItem); + return d->path; +} + +void QQuickPathItem::setPath(QQuickPath *path) +{ + Q_D(QQuickPathItem); + if (d->path == path) + return; + + if (d->path) + qmlobject_disconnect(d->path, QQuickPath, SIGNAL(changed()), + this, QQuickPathItem, SLOT(_q_pathChanged())); + d->path = path; + qmlobject_connect(d->path, QQuickPath, SIGNAL(changed()), + this, QQuickPathItem, SLOT(_q_pathChanged())); + + d->dirty |= QQuickPathItemPrivate::DirtyPath; + emit pathChanged(); + polish(); +} + +void QQuickPathItemPrivate::_q_pathChanged() +{ + Q_Q(QQuickPathItem); + dirty |= DirtyPath; + q->polish(); +} + +QColor QQuickPathItem::strokeColor() const +{ + Q_D(const QQuickPathItem); + return d->strokeColor; +} + +void QQuickPathItem::setStrokeColor(const QColor &color) +{ + Q_D(QQuickPathItem); + if (d->strokeColor != color) { + d->strokeColor = color; + d->dirty |= QQuickPathItemPrivate::DirtyStrokeColor; + emit strokeColorChanged(); + polish(); + } +} + +qreal QQuickPathItem::strokeWidth() const +{ + Q_D(const QQuickPathItem); + return d->strokeWidth; +} + +void QQuickPathItem::setStrokeWidth(qreal w) +{ + Q_D(QQuickPathItem); + if (d->strokeWidth != w) { + d->strokeWidth = w; + d->dirty |= QQuickPathItemPrivate::DirtyStrokeWidth; + emit strokeWidthChanged(); + polish(); + } +} + +QColor QQuickPathItem::fillColor() const +{ + Q_D(const QQuickPathItem); + return d->fillColor; +} + +void QQuickPathItem::setFillColor(const QColor &color) +{ + Q_D(QQuickPathItem); + if (d->fillColor != color) { + d->fillColor = color; + d->dirty |= QQuickPathItemPrivate::DirtyFillColor; + emit fillColorChanged(); + polish(); + } +} + +QQuickPathItem::FillRule QQuickPathItem::fillRule() const +{ + Q_D(const QQuickPathItem); + return d->fillRule; +} + +void QQuickPathItem::setFillRule(FillRule fillRule) +{ + Q_D(QQuickPathItem); + if (d->fillRule != fillRule) { + d->fillRule = fillRule; + d->dirty |= QQuickPathItemPrivate::DirtyFillRule; + emit fillRuleChanged(); + polish(); + } +} + +QQuickPathItem::JoinStyle QQuickPathItem::joinStyle() const +{ + Q_D(const QQuickPathItem); + return d->joinStyle; +} + +void QQuickPathItem::setJoinStyle(JoinStyle style) +{ + Q_D(QQuickPathItem); + if (d->joinStyle != style) { + d->joinStyle = style; + d->dirty |= QQuickPathItemPrivate::DirtyStyle; + emit joinStyleChanged(); + polish(); + } +} + +int QQuickPathItem::miterLimit() const +{ + Q_D(const QQuickPathItem); + return d->miterLimit; +} + +void QQuickPathItem::setMiterLimit(int limit) +{ + Q_D(QQuickPathItem); + if (d->miterLimit != limit) { + d->miterLimit = limit; + d->dirty |= QQuickPathItemPrivate::DirtyStyle; + emit miterLimitChanged(); + polish(); + } +} + +QQuickPathItem::CapStyle QQuickPathItem::capStyle() const +{ + Q_D(const QQuickPathItem); + return d->capStyle; +} + +void QQuickPathItem::setCapStyle(CapStyle style) +{ + Q_D(QQuickPathItem); + if (d->capStyle != style) { + d->capStyle = style; + d->dirty |= QQuickPathItemPrivate::DirtyStyle; + emit capStyleChanged(); + polish(); + } +} + +QQuickPathItem::StrokeStyle QQuickPathItem::strokeStyle() const +{ + Q_D(const QQuickPathItem); + return d->strokeStyle; +} + +void QQuickPathItem::setStrokeStyle(StrokeStyle style) +{ + Q_D(QQuickPathItem); + if (d->strokeStyle != style) { + d->strokeStyle = style; + d->dirty |= QQuickPathItemPrivate::DirtyDash; + emit strokeStyleChanged(); + polish(); + } +} + +qreal QQuickPathItem::dashOffset() const +{ + Q_D(const QQuickPathItem); + return d->dashOffset; +} + +void QQuickPathItem::setDashOffset(qreal offset) +{ + Q_D(QQuickPathItem); + if (d->dashOffset != offset) { + d->dashOffset = offset; + d->dirty |= QQuickPathItemPrivate::DirtyDash; + emit dashOffsetChanged(); + polish(); + } +} + +QVector QQuickPathItem::dashPattern() const +{ + Q_D(const QQuickPathItem); + return d->dashPattern; +} + +void QQuickPathItem::setDashPattern(const QVector &array) +{ + Q_D(QQuickPathItem); + if (d->dashPattern != array) { + d->dashPattern = array; + d->dirty |= QQuickPathItemPrivate::DirtyDash; + emit dashPatternChanged(); + polish(); + } +} + +QQuickPathGradient *QQuickPathItem::fillGradient() const +{ + Q_D(const QQuickPathItem); + return d->fillGradient; +} + +void QQuickPathItem::setFillGradient(QQuickPathGradient *gradient) +{ + Q_D(QQuickPathItem); + if (d->fillGradient != gradient) { + if (d->fillGradient) + qmlobject_disconnect(d->fillGradient, QQuickPathGradient, SIGNAL(updated()), + this, QQuickPathItem, SLOT(_q_fillGradientChanged())); + d->fillGradient = gradient; + if (d->fillGradient) + qmlobject_connect(d->fillGradient, QQuickPathGradient, SIGNAL(updated()), + this, QQuickPathItem, SLOT(_q_fillGradientChanged())); + d->dirty |= QQuickPathItemPrivate::DirtyFillGradient; + polish(); + } +} + +void QQuickPathItemPrivate::_q_fillGradientChanged() +{ + Q_Q(QQuickPathItem); + dirty |= DirtyFillGradient; + q->polish(); +} + +void QQuickPathItem::resetFillGradient() +{ + setFillGradient(nullptr); +} + +void QQuickPathItem::updatePolish() +{ + Q_D(QQuickPathItem); + + if (!d->dirty) + return; + + if (!d->renderer) { + d->createRenderer(); + if (!d->renderer) + return; + emit rendererChanged(); + } + + // endSync() is where expensive calculations may happen, depending on the + // backend. Therefore do this only when the item is visible. + if (isVisible()) + d->sync(); + + update(); +} + +void QQuickPathItem::itemChange(ItemChange change, const ItemChangeData &data) +{ + // sync may have been deferred; do it now if the item became visible + if (change == ItemVisibleHasChanged && data.boolValue) + polish(); + + QQuickItem::itemChange(change, data); +} + +QSGNode *QQuickPathItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *) +{ + // Called on the render thread, with the gui thread blocked. We can now + // safely access gui thread data. + + Q_D(QQuickPathItem); + if (d->renderer) { + if (!node) + node = d->createRenderNode(); + d->renderer->updatePathRenderNode(); + } + return node; +} + +// the renderer object lives on the gui thread +void QQuickPathItemPrivate::createRenderer() +{ + Q_Q(QQuickPathItem); + QSGRendererInterface *ri = q->window()->rendererInterface(); + if (!ri) + return; + + switch (ri->graphicsApi()) { +#ifndef QT_NO_OPENGL + case QSGRendererInterface::OpenGL: + if (QQuickPathItemNvprRenderNode::isSupported()) { + rendererType = QQuickPathItem::NvprRenderer; + renderer = new QQuickPathItemNvprRenderer; + } else { + rendererType = QQuickPathItem::GeometryRenderer; + renderer = new QQuickPathItemGenericRenderer(q); + } + break; +#endif + case QSGRendererInterface::Software: + rendererType = QQuickPathItem::SoftwareRenderer; + renderer = new QQuickPathItemSoftwareRenderer; + break; + default: + qWarning("No path backend for this graphics API yet"); + break; + } +} + +// the node lives on the render thread +QSGNode *QQuickPathItemPrivate::createRenderNode() +{ + Q_Q(QQuickPathItem); + QSGNode *node = nullptr; + if (!q->window()) + return node; + QSGRendererInterface *ri = q->window()->rendererInterface(); + if (!ri) + return node; + + const bool hasFill = fillColor != Qt::transparent; + const bool hasStroke = !qFuzzyIsNull(strokeWidth) && strokeColor != Qt::transparent; + + switch (ri->graphicsApi()) { +#ifndef QT_NO_OPENGL + case QSGRendererInterface::OpenGL: + if (QQuickPathItemNvprRenderNode::isSupported()) { + node = new QQuickPathItemNvprRenderNode(q); + static_cast(renderer)->setNode( + static_cast(node)); + } else { + node = new QQuickPathItemGenericRootRenderNode(q->window(), hasFill, hasStroke); + static_cast(renderer)->setRootNode( + static_cast(node)); + } + break; +#endif + case QSGRendererInterface::Software: + node = new QQuickPathItemSoftwareRenderNode(q); + static_cast(renderer)->setNode( + static_cast(node)); + break; + default: + qWarning("No path backend for this graphics API yet"); + break; + } + + return node; +} + +void QQuickPathItemPrivate::sync() +{ + renderer->beginSync(); + + if (dirty & QQuickPathItemPrivate::DirtyPath) + renderer->setPath(path); + if (dirty & DirtyStrokeColor) + renderer->setStrokeColor(strokeColor); + if (dirty & DirtyStrokeWidth) + renderer->setStrokeWidth(strokeWidth); + if (dirty & DirtyFillColor) + renderer->setFillColor(fillColor); + if (dirty & DirtyFillRule) + renderer->setFillRule(fillRule); + if (dirty & DirtyStyle) { + renderer->setJoinStyle(joinStyle, miterLimit); + renderer->setCapStyle(capStyle); + } + if (dirty & DirtyDash) + renderer->setStrokeStyle(strokeStyle, dashOffset, dashPattern); + if (dirty & DirtyFillGradient) + renderer->setFillGradient(fillGradient); + + renderer->endSync(); + dirty = 0; +} + +// ***** gradient support ***** + +QQuickPathGradientStop::QQuickPathGradientStop(QObject *parent) + : QObject(parent), + m_position(0), + m_color(Qt::black) +{ +} + +qreal QQuickPathGradientStop::position() const +{ + return m_position; +} + +void QQuickPathGradientStop::setPosition(qreal position) +{ + if (m_position != position) { + m_position = position; + if (QQuickPathGradient *grad = qobject_cast(parent())) + emit grad->updated(); + } +} + +QColor QQuickPathGradientStop::color() const +{ + return m_color; +} + +void QQuickPathGradientStop::setColor(const QColor &color) +{ + if (m_color != color) { + m_color = color; + if (QQuickPathGradient *grad = qobject_cast(parent())) + emit grad->updated(); + } +} + +QQuickPathGradient::QQuickPathGradient(QObject *parent) + : QObject(parent), + m_spread(PadSpread) +{ +} + +void QQuickPathGradient::appendStop(QQmlListProperty *list, QObject *stop) +{ + QQuickPathGradientStop *sstop = qobject_cast(stop); + if (!sstop) { + qWarning("Gradient stop list only supports QQuickPathGradientStop elements"); + return; + } + QQuickPathGradient *grad = qobject_cast(list->object); + Q_ASSERT(grad); + sstop->setParent(grad); + grad->m_stops.append(sstop); +} + +QQmlListProperty QQuickPathGradient::stops() +{ + return QQmlListProperty(this, nullptr, &QQuickPathGradient::appendStop, nullptr, nullptr, nullptr); +} + +QGradientStops QQuickPathGradient::sortedGradientStops() const +{ + QGradientStops result; + for (int i = 0; i < m_stops.count(); ++i) { + QQuickPathGradientStop *s = static_cast(m_stops[i]); + int j = 0; + while (j < result.count() && result[j].first < s->position()) + ++j; + result.insert(j, QGradientStop(s->position(), s->color())); + } + return result; +} + +QQuickPathGradient::SpreadMode QQuickPathGradient::spread() const +{ + return m_spread; +} + +void QQuickPathGradient::setSpread(SpreadMode mode) +{ + if (m_spread != mode) { + m_spread = mode; + emit spreadChanged(); + emit updated(); + } +} + +QQuickPathLinearGradient::QQuickPathLinearGradient(QObject *parent) + : QQuickPathGradient(parent) +{ +} + +qreal QQuickPathLinearGradient::x1() const +{ + return m_start.x(); +} + +void QQuickPathLinearGradient::setX1(qreal v) +{ + if (m_start.x() != v) { + m_start.setX(v); + emit x1Changed(); + emit updated(); + } +} + +qreal QQuickPathLinearGradient::y1() const +{ + return m_start.y(); +} + +void QQuickPathLinearGradient::setY1(qreal v) +{ + if (m_start.y() != v) { + m_start.setY(v); + emit y1Changed(); + emit updated(); + } +} + +qreal QQuickPathLinearGradient::x2() const +{ + return m_end.x(); +} + +void QQuickPathLinearGradient::setX2(qreal v) +{ + if (m_end.x() != v) { + m_end.setX(v); + emit x2Changed(); + emit updated(); + } +} + +qreal QQuickPathLinearGradient::y2() const +{ + return m_end.y(); +} + +void QQuickPathLinearGradient::setY2(qreal v) +{ + if (m_end.y() != v) { + m_end.setY(v); + emit y2Changed(); + emit updated(); + } +} + +#ifndef QT_NO_OPENGL + +// contexts sharing with each other get the same cache instance +class QQuickPathItemGradientCacheWrapper +{ +public: + QQuickPathItemGradientCache *get(QOpenGLContext *context) + { + return m_resource.value(context); + } + +private: + QOpenGLMultiGroupSharedResource m_resource; +}; + +QQuickPathItemGradientCache *QQuickPathItemGradientCache::currentCache() +{ + static QQuickPathItemGradientCacheWrapper qt_path_gradient_caches; + return qt_path_gradient_caches.get(QOpenGLContext::currentContext()); +} + +// let QOpenGLContext manage the lifetime of the cached textures +QQuickPathItemGradientCache::~QQuickPathItemGradientCache() +{ + m_cache.clear(); +} + +void QQuickPathItemGradientCache::invalidateResource() +{ + m_cache.clear(); +} + +void QQuickPathItemGradientCache::freeResource(QOpenGLContext *) +{ + qDeleteAll(m_cache); + m_cache.clear(); +} + +static void generateGradientColorTable(const QQuickPathItemGradientCache::GradientDesc &gradient, + uint *colorTable, int size, float opacity) +{ + int pos = 0; + const QGradientStops &s = gradient.stops; + const bool colorInterpolation = true; + + uint alpha = qRound(opacity * 256); + uint current_color = ARGB_COMBINE_ALPHA(s[0].second.rgba(), alpha); + qreal incr = 1.0 / qreal(size); + qreal fpos = 1.5 * incr; + colorTable[pos++] = ARGB2RGBA(qPremultiply(current_color)); + + while (fpos <= s.first().first) { + colorTable[pos] = colorTable[pos - 1]; + pos++; + fpos += incr; + } + + if (colorInterpolation) + current_color = qPremultiply(current_color); + + const int sLast = s.size() - 1; + for (int i = 0; i < sLast; ++i) { + qreal delta = 1/(s[i+1].first - s[i].first); + uint next_color = ARGB_COMBINE_ALPHA(s[i + 1].second.rgba(), alpha); + if (colorInterpolation) + next_color = qPremultiply(next_color); + + while (fpos < s[i+1].first && pos < size) { + int dist = int(256 * ((fpos - s[i].first) * delta)); + int idist = 256 - dist; + if (colorInterpolation) + colorTable[pos] = ARGB2RGBA(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist)); + else + colorTable[pos] = ARGB2RGBA(qPremultiply(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist))); + ++pos; + fpos += incr; + } + current_color = next_color; + } + + Q_ASSERT(s.size() > 0); + + uint last_color = ARGB2RGBA(qPremultiply(ARGB_COMBINE_ALPHA(s[sLast].second.rgba(), alpha))); + for ( ; pos < size; ++pos) + colorTable[pos] = last_color; + + colorTable[size-1] = last_color; +} + +QSGTexture *QQuickPathItemGradientCache::get(const GradientDesc &grad) +{ + QSGPlainTexture *tx = m_cache[grad]; + if (!tx) { + QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); + GLuint id; + f->glGenTextures(1, &id); + f->glBindTexture(GL_TEXTURE_2D, id); + static const uint W = 1024; // texture size is 1024x1 + uint buf[W]; + generateGradientColorTable(grad, buf, W, 1.0f); + f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, W, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf); + tx = new QSGPlainTexture; + tx->setTextureId(id); + switch (grad.spread) { + case QQuickPathGradient::PadSpread: + tx->setHorizontalWrapMode(QSGTexture::ClampToEdge); + tx->setVerticalWrapMode(QSGTexture::ClampToEdge); + break; + case QQuickPathGradient::RepeatSpread: + tx->setHorizontalWrapMode(QSGTexture::Repeat); + tx->setVerticalWrapMode(QSGTexture::Repeat); + break; + case QQuickPathGradient::ReflectSpread: + tx->setHorizontalWrapMode(QSGTexture::MirroredRepeat); + tx->setVerticalWrapMode(QSGTexture::MirroredRepeat); + break; + default: + qWarning("Unknown gradient spread mode %d", grad.spread); + break; + } + m_cache[grad] = tx; + } + return tx; +} + +#endif // QT_NO_OPENGL + +QT_END_NAMESPACE + +#include "moc_qquickpathitem_p.cpp" diff --git a/src/quick/items/qquickpathitem_p.h b/src/quick/items/qquickpathitem_p.h new file mode 100644 index 0000000000..39b407cf87 --- /dev/null +++ b/src/quick/items/qquickpathitem_p.h @@ -0,0 +1,281 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPATHITEM_P_H +#define QQUICKPATHITEM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickitem.h" + +#include +#include +#include + +QT_REQUIRE_CONFIG(quick_path); + +QT_BEGIN_NAMESPACE + +class QQuickPathItemPrivate; + +class Q_QUICK_PRIVATE_EXPORT QQuickPathGradientStop : public QObject +{ + Q_OBJECT + Q_PROPERTY(qreal position READ position WRITE setPosition) + Q_PROPERTY(QColor color READ color WRITE setColor) + +public: + QQuickPathGradientStop(QObject *parent = nullptr); + + qreal position() const; + void setPosition(qreal position); + + QColor color() const; + void setColor(const QColor &color); + +private: + qreal m_position; + QColor m_color; +}; + +class Q_QUICK_PRIVATE_EXPORT QQuickPathGradient : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQmlListProperty stops READ stops) + Q_PROPERTY(SpreadMode spread READ spread WRITE setSpread NOTIFY spreadChanged) + Q_CLASSINFO("DefaultProperty", "stops") + +public: + enum SpreadMode { + PadSpread, + RepeatSpread, + ReflectSpread + }; + Q_ENUM(SpreadMode) + + QQuickPathGradient(QObject *parent = nullptr); + + QQmlListProperty stops(); + + QGradientStops sortedGradientStops() const; + + SpreadMode spread() const; + void setSpread(SpreadMode mode); + +signals: + void updated(); + void spreadChanged(); + +private: + static void appendStop(QQmlListProperty *list, QObject *stop); + + QVector m_stops; + SpreadMode m_spread; +}; + +class Q_QUICK_PRIVATE_EXPORT QQuickPathLinearGradient : public QQuickPathGradient +{ + Q_OBJECT + Q_PROPERTY(qreal x1 READ x1 WRITE setX1 NOTIFY x1Changed) + Q_PROPERTY(qreal y1 READ y1 WRITE setY1 NOTIFY y1Changed) + Q_PROPERTY(qreal x2 READ x2 WRITE setX2 NOTIFY x2Changed) + Q_PROPERTY(qreal y2 READ y2 WRITE setY2 NOTIFY y2Changed) + Q_CLASSINFO("DefaultProperty", "stops") + +public: + QQuickPathLinearGradient(QObject *parent = nullptr); + + qreal x1() const; + void setX1(qreal v); + qreal y1() const; + void setY1(qreal v); + qreal x2() const; + void setX2(qreal v); + qreal y2() const; + void setY2(qreal v); + +signals: + void x1Changed(); + void y1Changed(); + void x2Changed(); + void y2Changed(); + +private: + QPointF m_start; + QPointF m_end; +}; + +class Q_QUICK_PRIVATE_EXPORT QQuickPathItem : public QQuickItem +{ + Q_OBJECT + + Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged) + + Q_PROPERTY(QQuickPath *path READ path WRITE setPath NOTIFY pathChanged) + + Q_PROPERTY(QColor strokeColor READ strokeColor WRITE setStrokeColor NOTIFY strokeColorChanged) + Q_PROPERTY(qreal strokeWidth READ strokeWidth WRITE setStrokeWidth NOTIFY strokeWidthChanged) + Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged) + Q_PROPERTY(FillRule fillRule READ fillRule WRITE setFillRule NOTIFY fillRuleChanged) + Q_PROPERTY(JoinStyle joinStyle READ joinStyle WRITE setJoinStyle NOTIFY joinStyleChanged) + Q_PROPERTY(int miterLimit READ miterLimit WRITE setMiterLimit NOTIFY miterLimitChanged) + Q_PROPERTY(CapStyle capStyle READ capStyle WRITE setCapStyle NOTIFY capStyleChanged) + Q_PROPERTY(StrokeStyle strokeStyle READ strokeStyle WRITE setStrokeStyle NOTIFY strokeStyleChanged) + Q_PROPERTY(qreal dashOffset READ dashOffset WRITE setDashOffset NOTIFY dashOffsetChanged) + Q_PROPERTY(QVector dashPattern READ dashPattern WRITE setDashPattern NOTIFY dashPatternChanged) + Q_PROPERTY(QQuickPathGradient *fillGradient READ fillGradient WRITE setFillGradient RESET resetFillGradient) + +public: + enum FillRule { + OddEvenFill = Qt::OddEvenFill, + WindingFill = Qt::WindingFill + }; + Q_ENUM(FillRule) + + enum JoinStyle { + MiterJoin = Qt::MiterJoin, + BevelJoin = Qt::BevelJoin, + RoundJoin = Qt::RoundJoin + }; + Q_ENUM(JoinStyle) + + enum CapStyle { + FlatCap = Qt::FlatCap, + SquareCap = Qt::SquareCap, + RoundCap = Qt::RoundCap + }; + Q_ENUM(CapStyle) + + enum StrokeStyle { + SolidLine = Qt::SolidLine, + DashLine = Qt::DashLine + }; + Q_ENUM(StrokeStyle) + + enum RendererType { + UnknownRenderer, + GeometryRenderer, + NvprRenderer, + SoftwareRenderer + }; + Q_ENUM(RendererType) + + QQuickPathItem(QQuickItem *parent = nullptr); + ~QQuickPathItem(); + + RendererType rendererType() const; + + QQuickPath *path() const; + void setPath(QQuickPath *path); + + QColor strokeColor() const; + void setStrokeColor(const QColor &color); + + qreal strokeWidth() const; + void setStrokeWidth(qreal w); + + QColor fillColor() const; + void setFillColor(const QColor &color); + + FillRule fillRule() const; + void setFillRule(FillRule fillRule); + + JoinStyle joinStyle() const; + void setJoinStyle(JoinStyle style); + + int miterLimit() const; + void setMiterLimit(int limit); + + CapStyle capStyle() const; + void setCapStyle(CapStyle style); + + StrokeStyle strokeStyle() const; + void setStrokeStyle(StrokeStyle style); + + qreal dashOffset() const; + void setDashOffset(qreal offset); + + QVector dashPattern() const; + void setDashPattern(const QVector &array); + + QQuickPathGradient *fillGradient() const; + void setFillGradient(QQuickPathGradient *gradient); + void resetFillGradient(); + +protected: + QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override; + void updatePolish() override; + void itemChange(ItemChange change, const ItemChangeData &data) override; + +Q_SIGNALS: + void rendererChanged(); + void pathChanged(); + void strokeColorChanged(); + void strokeWidthChanged(); + void fillColorChanged(); + void fillRuleChanged(); + void joinStyleChanged(); + void miterLimitChanged(); + void capStyleChanged(); + void strokeStyleChanged(); + void dashOffsetChanged(); + void dashPatternChanged(); + void fillGradientChanged(); + +private: + Q_DISABLE_COPY(QQuickPathItem) + Q_DECLARE_PRIVATE(QQuickPathItem) + Q_PRIVATE_SLOT(d_func(), void _q_pathChanged()) + Q_PRIVATE_SLOT(d_func(), void _q_fillGradientChanged()) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPathItem) + +#endif // QQUICKPATHITEM_P_H diff --git a/src/quick/items/qquickpathitem_p_p.h b/src/quick/items/qquickpathitem_p_p.h new file mode 100644 index 0000000000..366628d867 --- /dev/null +++ b/src/quick/items/qquickpathitem_p_p.h @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPATHITEM_P_P_H +#define QQUICKPATHITEM_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickpathitem_p.h" +#include "qquickitem_p.h" +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QSGPlainTexture; + +class QQuickAbstractPathRenderer +{ +public: + virtual ~QQuickAbstractPathRenderer() { } + + // Gui thread + virtual void beginSync() = 0; + virtual void setPath(const QQuickPath *path) = 0; + virtual void setStrokeColor(const QColor &color) = 0; + virtual void setStrokeWidth(qreal w) = 0; + virtual void setFillColor(const QColor &color) = 0; + virtual void setFillRule(QQuickPathItem::FillRule fillRule) = 0; + virtual void setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit) = 0; + virtual void setCapStyle(QQuickPathItem::CapStyle capStyle) = 0; + virtual void setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) = 0; + virtual void setFillGradient(QQuickPathGradient *gradient) = 0; + virtual void endSync() = 0; + + // Render thread, with gui blocked + virtual void updatePathRenderNode() = 0; +}; + +class QQuickPathItemPrivate : public QQuickItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickPathItem) + +public: + enum Dirty { + DirtyPath = 0x01, + DirtyStrokeColor = 0x02, + DirtyStrokeWidth = 0x04, + DirtyFillColor = 0x08, + DirtyFillRule = 0x10, + DirtyStyle = 0x20, + DirtyDash = 0x40, + DirtyFillGradient = 0x80, + + DirtyAll = 0xFF + }; + + QQuickPathItemPrivate(); + ~QQuickPathItemPrivate(); + + void createRenderer(); + QSGNode *createRenderNode(); + void sync(); + + void _q_pathChanged(); + void _q_fillGradientChanged(); + + QQuickPathItem::RendererType rendererType; + QQuickAbstractPathRenderer *renderer; + QQuickPath *path; + int dirty; + QColor strokeColor; + qreal strokeWidth; + QColor fillColor; + QQuickPathItem::FillRule fillRule; + QQuickPathItem::JoinStyle joinStyle; + int miterLimit; + QQuickPathItem::CapStyle capStyle; + QQuickPathItem::StrokeStyle strokeStyle; + qreal dashOffset; + QVector dashPattern; + QQuickPathGradient *fillGradient; +}; + +#ifndef QT_NO_OPENGL + +class QQuickPathItemGradientCache : public QOpenGLSharedResource +{ +public: + struct GradientDesc { + QGradientStops stops; + QPointF start; + QPointF end; + QQuickPathGradient::SpreadMode spread; + bool operator==(const GradientDesc &other) const + { + return start == other.start && end == other.end && spread == other.spread + && stops == other.stops; + } + }; + + QQuickPathItemGradientCache(QOpenGLContext *context) : QOpenGLSharedResource(context->shareGroup()) { } + ~QQuickPathItemGradientCache(); + + void invalidateResource() override; + void freeResource(QOpenGLContext *) override; + + QSGTexture *get(const GradientDesc &grad); + + static QQuickPathItemGradientCache *currentCache(); + +private: + QHash m_cache; +}; + +inline uint qHash(const QQuickPathItemGradientCache::GradientDesc &v, uint seed = 0) +{ + uint h = seed; + h += v.start.x() + v.end.y() + v.spread; + for (int i = 0; i < 3 && i < v.stops.count(); ++i) + h += v.stops[i].second.rgba(); + return h; +} + +#endif // QT_NO_OPENGL + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp new file mode 100644 index 0000000000..9bd03c0e51 --- /dev/null +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -0,0 +1,510 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickpathitemgenericrenderer_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +static const qreal SCALE = 100; + +struct ColoredVertex // must match QSGGeometry::ColoredPoint2D +{ + float x, y; + QQuickPathItemGenericRenderer::Color4ub color; + void set(float nx, float ny, QQuickPathItemGenericRenderer::Color4ub ncolor) + { + x = nx; y = ny; color = ncolor; + } +}; + +static inline QQuickPathItemGenericRenderer::Color4ub colorToColor4ub(const QColor &c) +{ + QQuickPathItemGenericRenderer::Color4ub color = { + uchar(qRound(c.redF() * c.alphaF() * 255)), + uchar(qRound(c.greenF() * c.alphaF() * 255)), + uchar(qRound(c.blueF() * c.alphaF() * 255)), + uchar(qRound(c.alphaF() * 255)) + }; + return color; +} + +QQuickPathItemGenericRootRenderNode::QQuickPathItemGenericRootRenderNode(QQuickWindow *window, + bool hasFill, + bool hasStroke) + : m_fillNode(nullptr), + m_strokeNode(nullptr) +{ + if (hasFill) { + m_fillNode = new QQuickPathItemGenericRenderNode(window, this); + appendChildNode(m_fillNode); + } + if (hasStroke) { + m_strokeNode = new QQuickPathItemGenericRenderNode(window, this); + appendChildNode(m_strokeNode); + } +} + +QQuickPathItemGenericRootRenderNode::~QQuickPathItemGenericRootRenderNode() +{ +} + +QQuickPathItemGenericRenderNode::QQuickPathItemGenericRenderNode(QQuickWindow *window, + QQuickPathItemGenericRootRenderNode *rootNode) + : m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0, 0), + m_window(window), + m_rootNode(rootNode), + m_material(nullptr) +{ + setGeometry(&m_geometry); + activateMaterial(MatSolidColor); +} + +QQuickPathItemGenericRenderNode::~QQuickPathItemGenericRenderNode() +{ +} + +void QQuickPathItemGenericRenderNode::activateMaterial(Material m) +{ + switch (m) { + case MatSolidColor: + // Use vertexcolor material. Items with different colors remain batchable + // this way, at the expense of having to provide per-vertex color values. + if (!m_solidColorMaterial) + m_solidColorMaterial.reset(QQuickPathItemGenericMaterialFactory::createVertexColor(m_window)); + m_material = m_solidColorMaterial.data(); + break; + case MatLinearGradient: + if (!m_linearGradientMaterial) + m_linearGradientMaterial.reset(QQuickPathItemGenericMaterialFactory::createLinearGradient(m_window, this)); + m_material = m_linearGradientMaterial.data(); + break; + default: + qWarning("Unknown material %d", m); + return; + } + + if (material() != m_material) + setMaterial(m_material); +} + +// sync, and so triangulation too, happens on the gui thread +void QQuickPathItemGenericRenderer::beginSync() +{ + m_syncDirty = 0; +} + +void QQuickPathItemGenericRenderer::setPath(const QQuickPath *path) +{ + m_path = path ? path->path() : QPainterPath(); + m_syncDirty |= DirtyGeom; +} + +void QQuickPathItemGenericRenderer::setStrokeColor(const QColor &color) +{ + m_strokeColor = colorToColor4ub(color); + m_syncDirty |= DirtyColor; +} + +void QQuickPathItemGenericRenderer::setStrokeWidth(qreal w) +{ + m_pen.setWidthF(w); + m_syncDirty |= DirtyGeom; +} + +void QQuickPathItemGenericRenderer::setFillColor(const QColor &color) +{ + m_fillColor = colorToColor4ub(color); + m_syncDirty |= DirtyColor; +} + +void QQuickPathItemGenericRenderer::setFillRule(QQuickPathItem::FillRule fillRule) +{ + m_fillRule = Qt::FillRule(fillRule); + m_syncDirty |= DirtyGeom; +} + +void QQuickPathItemGenericRenderer::setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit) +{ + m_pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); + m_pen.setMiterLimit(miterLimit); + m_syncDirty |= DirtyGeom; +} + +void QQuickPathItemGenericRenderer::setCapStyle(QQuickPathItem::CapStyle capStyle) +{ + m_pen.setCapStyle(Qt::PenCapStyle(capStyle)); + m_syncDirty |= DirtyGeom; +} + +void QQuickPathItemGenericRenderer::setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) +{ + m_pen.setStyle(Qt::PenStyle(strokeStyle)); + if (strokeStyle == QQuickPathItem::DashLine) { + m_pen.setDashPattern(dashPattern); + m_pen.setDashOffset(dashOffset); + } + m_syncDirty |= DirtyGeom; +} + +void QQuickPathItemGenericRenderer::setFillGradient(QQuickPathGradient *gradient) +{ + m_fillGradientActive = gradient != nullptr; + if (gradient) { + m_fillGradient.stops = gradient->sortedGradientStops(); + m_fillGradient.spread = gradient->spread(); + if (QQuickPathLinearGradient *g = qobject_cast(gradient)) { + m_fillGradient.start = QPointF(g->x1(), g->y1()); + m_fillGradient.end = QPointF(g->x2(), g->y2()); + } else { + Q_UNREACHABLE(); + } + } + m_syncDirty |= DirtyFillGradient; +} + +void QQuickPathItemGenericRenderer::endSync() +{ + if (!m_syncDirty) + return; + + // Use a shadow dirty flag in order to avoid losing state in case there are + // multiple syncs with different dirty flags before we get to + // updatePathRenderNode() on the render thread (with the gui thread + // blocked). For our purposes here m_syncDirty is still required since + // geometry regeneration must only happen when there was an actual change + // in this particular sync round. + m_effectiveDirty |= m_syncDirty; + + if (m_path.isEmpty()) { + m_fillVertices.clear(); + m_fillIndices.clear(); + m_strokeVertices.clear(); + return; + } + + triangulateFill(); + triangulateStroke(); +} + +void QQuickPathItemGenericRenderer::triangulateFill() +{ + m_path.setFillRule(m_fillRule); + + const QVectorPath &vp = qtVectorPathForPath(m_path); + + QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(SCALE, SCALE)); + const int vertexCount = ts.vertices.count() / 2; // just a qreal vector with x,y hence the / 2 + m_fillVertices.resize(vertexCount); + ColoredVertex *vdst = reinterpret_cast(m_fillVertices.data()); + const qreal *vsrc = ts.vertices.constData(); + for (int i = 0; i < vertexCount; ++i) + vdst[i].set(vsrc[i * 2] / SCALE, vsrc[i * 2 + 1] / SCALE, m_fillColor); + + m_fillIndices.resize(ts.indices.size()); + quint16 *idst = m_fillIndices.data(); + if (ts.indices.type() == QVertexIndexVector::UnsignedShort) { + memcpy(idst, ts.indices.data(), m_fillIndices.count() * sizeof(quint16)); + } else { + const quint32 *isrc = (const quint32 *) ts.indices.data(); + for (int i = 0; i < m_fillIndices.count(); ++i) + idst[i] = isrc[i]; + } +} + +void QQuickPathItemGenericRenderer::triangulateStroke() +{ + const QVectorPath &vp = qtVectorPathForPath(m_path); + + const QRectF clip(0, 0, m_item->width(), m_item->height()); + const qreal inverseScale = 1.0 / SCALE; + m_stroker.setInvScale(inverseScale); + if (m_pen.style() == Qt::SolidLine) { + m_stroker.process(vp, m_pen, clip, 0); + } else { + m_dashStroker.setInvScale(inverseScale); + m_dashStroker.process(vp, m_pen, clip, 0); + QVectorPath dashStroke(m_dashStroker.points(), m_dashStroker.elementCount(), + m_dashStroker.elementTypes(), 0); + m_stroker.process(dashStroke, m_pen, clip, 0); + } + + if (!m_stroker.vertexCount()) { + m_strokeVertices.clear(); + return; + } + + const int vertexCount = m_stroker.vertexCount() / 2; // just a float vector with x,y hence the / 2 + m_strokeVertices.resize(vertexCount); + ColoredVertex *vdst = reinterpret_cast(m_strokeVertices.data()); + const float *vsrc = m_stroker.vertices(); + for (int i = 0; i < vertexCount; ++i) + vdst[i].set(vsrc[i * 2], vsrc[i * 2 + 1], m_strokeColor); +} + +void QQuickPathItemGenericRenderer::setRootNode(QQuickPathItemGenericRootRenderNode *rn) +{ + if (m_rootNode != rn) { + m_rootNode = rn; + // Scenegraph nodes can be destroyed and then replaced by new ones over + // time; hence it is important to mark everything dirty for + // updatePathRenderNode(). We can assume the renderer has a full sync + // of the data at this point. + m_effectiveDirty = DirtyAll; + } +} + +// on the render thread with gui blocked +void QQuickPathItemGenericRenderer::updatePathRenderNode() +{ + if (!m_effectiveDirty || !m_rootNode) + return; + + if (m_fillColor.a == 0) { + delete m_rootNode->m_fillNode; + m_rootNode->m_fillNode = nullptr; + } else if (!m_rootNode->m_fillNode) { + m_rootNode->m_fillNode = new QQuickPathItemGenericRenderNode(m_item->window(), m_rootNode); + if (m_rootNode->m_strokeNode) + m_rootNode->removeChildNode(m_rootNode->m_strokeNode); + m_rootNode->appendChildNode(m_rootNode->m_fillNode); + if (m_rootNode->m_strokeNode) + m_rootNode->appendChildNode(m_rootNode->m_strokeNode); + m_effectiveDirty |= DirtyGeom; + } + + if (qFuzzyIsNull(m_pen.widthF()) || m_strokeColor.a == 0) { + delete m_rootNode->m_strokeNode; + m_rootNode->m_strokeNode = nullptr; + } else if (!m_rootNode->m_strokeNode) { + m_rootNode->m_strokeNode = new QQuickPathItemGenericRenderNode(m_item->window(), m_rootNode); + m_rootNode->appendChildNode(m_rootNode->m_strokeNode); + m_effectiveDirty |= DirtyGeom; + } + + updateFillNode(); + updateStrokeNode(); + + m_effectiveDirty = 0; +} + +void QQuickPathItemGenericRenderer::updateFillNode() +{ + if (!m_rootNode->m_fillNode) + return; + + QQuickPathItemGenericRenderNode *n = m_rootNode->m_fillNode; + QSGGeometry *g = &n->m_geometry; + if (m_fillVertices.isEmpty()) { + g->allocate(0, 0); + n->markDirty(QSGNode::DirtyGeometry); + return; + } + + if (m_fillGradientActive) { + n->activateMaterial(QQuickPathItemGenericRenderNode::MatLinearGradient); + if (m_effectiveDirty & DirtyFillGradient) { + // Make a copy of the data that will be accessed by the material on + // the render thread. + n->m_fillGradient = m_fillGradient; + // Gradients are implemented via a texture-based material. + n->markDirty(QSGNode::DirtyMaterial); + // stop here if only the gradient changed; no need to touch the geometry + if (!(m_effectiveDirty & DirtyGeom)) + return; + } + } else { + n->activateMaterial(QQuickPathItemGenericRenderNode::MatSolidColor); + // fast path for updating only color values when no change in vertex positions + if ((m_effectiveDirty & DirtyColor) && !(m_effectiveDirty & DirtyGeom)) { + ColoredVertex *vdst = reinterpret_cast(g->vertexData()); + for (int i = 0; i < g->vertexCount(); ++i) + vdst[i].set(vdst[i].x, vdst[i].y, m_fillColor); + n->markDirty(QSGNode::DirtyGeometry); + return; + } + } + + g->allocate(m_fillVertices.count(), m_fillIndices.count()); + g->setDrawingMode(QSGGeometry::DrawTriangles); + memcpy(g->vertexData(), m_fillVertices.constData(), g->vertexCount() * g->sizeOfVertex()); + memcpy(g->indexData(), m_fillIndices.constData(), g->indexCount() * g->sizeOfIndex()); + + n->markDirty(QSGNode::DirtyGeometry); +} + +void QQuickPathItemGenericRenderer::updateStrokeNode() +{ + if (!m_rootNode->m_strokeNode) + return; + if (m_effectiveDirty == DirtyFillGradient) // not applicable + return; + + QQuickPathItemGenericRenderNode *n = m_rootNode->m_strokeNode; + n->markDirty(QSGNode::DirtyGeometry); + + QSGGeometry *g = &n->m_geometry; + if (m_strokeVertices.isEmpty()) { + g->allocate(0, 0); + return; + } + + if ((m_effectiveDirty & DirtyColor) && !(m_effectiveDirty & DirtyGeom)) { + ColoredVertex *vdst = reinterpret_cast(g->vertexData()); + for (int i = 0; i < g->vertexCount(); ++i) + vdst[i].set(vdst[i].x, vdst[i].y, m_strokeColor); + return; + } + + g->allocate(m_strokeVertices.count(), 0); + g->setDrawingMode(QSGGeometry::DrawTriangleStrip); + memcpy(g->vertexData(), m_strokeVertices.constData(), g->vertexCount() * g->sizeOfVertex()); +} + +QSGMaterial *QQuickPathItemGenericMaterialFactory::createVertexColor(QQuickWindow *window) +{ + QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); + +#ifndef QT_NO_OPENGL + if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... + return new QSGVertexColorMaterial; +#endif + + qWarning("Vertex-color material: Unsupported graphics API %d", api); + return nullptr; +} + +QSGMaterial *QQuickPathItemGenericMaterialFactory::createLinearGradient(QQuickWindow *window, + QQuickPathItemGenericRenderNode *node) +{ + QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); + +#ifndef QT_NO_OPENGL + if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... + return new QQuickPathItemLinearGradientMaterial(node); +#endif + + qWarning("Linear gradient material: Unsupported graphics API %d", api); + return nullptr; +} + +#ifndef QT_NO_OPENGL + +QSGMaterialType QQuickPathItemLinearGradientShader::type; + +QQuickPathItemLinearGradientShader::QQuickPathItemLinearGradientShader() +{ + setShaderSourceFile(QOpenGLShader::Vertex, + QStringLiteral(":/qt-project.org/items/shaders/lineargradient.vert")); + setShaderSourceFile(QOpenGLShader::Fragment, + QStringLiteral(":/qt-project.org/items/shaders/lineargradient.frag")); +} + +void QQuickPathItemLinearGradientShader::initialize() +{ + m_opacityLoc = program()->uniformLocation("opacity"); + m_matrixLoc = program()->uniformLocation("matrix"); + m_gradStartLoc = program()->uniformLocation("gradStart"); + m_gradEndLoc = program()->uniformLocation("gradEnd"); +} + +void QQuickPathItemLinearGradientShader::updateState(const RenderState &state, QSGMaterial *mat, QSGMaterial *) +{ + QQuickPathItemLinearGradientMaterial *m = static_cast(mat); + + if (state.isOpacityDirty()) + program()->setUniformValue(m_opacityLoc, state.opacity()); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); + + QQuickPathItemGenericRenderNode *node = m->node(); + program()->setUniformValue(m_gradStartLoc, QVector2D(node->m_fillGradient.start)); + program()->setUniformValue(m_gradEndLoc, QVector2D(node->m_fillGradient.end)); + + QSGTexture *tx = QQuickPathItemGradientCache::currentCache()->get(node->m_fillGradient); + tx->bind(); +} + +char const *const *QQuickPathItemLinearGradientShader::attributeNames() const +{ + static const char *const attr[] = { "vertexCoord", "vertexColor", nullptr }; + return attr; +} + +int QQuickPathItemLinearGradientMaterial::compare(const QSGMaterial *other) const +{ + Q_ASSERT(other && type() == other->type()); + const QQuickPathItemLinearGradientMaterial *m = static_cast(other); + + QQuickPathItemGenericRenderNode *a = node(); + QQuickPathItemGenericRenderNode *b = m->node(); + Q_ASSERT(a && b); + if (a == b) + return 0; + + const QQuickPathItemGradientCache::GradientDesc *ga = &a->m_fillGradient; + const QQuickPathItemGradientCache::GradientDesc *gb = &b->m_fillGradient; + if (int d = ga->start.x() - gb->start.x()) + return d; + if (int d = ga->start.y() - gb->start.y()) + return d; + if (int d = ga->end.x() - gb->end.x()) + return d; + if (int d = ga->end.y() - gb->end.y()) + return d; + + if (int d = ga->stops.count() - gb->stops.count()) + return d; + + for (int i = 0; i < ga->stops.count(); ++i) { + if (int d = ga->stops[i].first - gb->stops[i].first) + return d; + if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba()) + return d; + } + + return 0; +} + +#endif // QT_NO_OPENGL + +QT_END_NAMESPACE diff --git a/src/quick/items/qquickpathitemgenericrenderer_p.h b/src/quick/items/qquickpathitemgenericrenderer_p.h new file mode 100644 index 0000000000..c186959c88 --- /dev/null +++ b/src/quick/items/qquickpathitemgenericrenderer_p.h @@ -0,0 +1,232 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPATHITEMGENERICRENDERER_P_H +#define QQUICKPATHITEMGENERICRENDERER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickpathitem_p_p.h" +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickPathItemGenericRootRenderNode; + +class QQuickPathItemGenericRenderer : public QQuickAbstractPathRenderer +{ +public: + enum Dirty { + DirtyGeom = 0x01, + DirtyColor = 0x02, + DirtyFillGradient = 0x04, + + DirtyAll = 0xFF + }; + + QQuickPathItemGenericRenderer(QQuickItem *item) + : m_item(item), + m_rootNode(nullptr), + m_effectiveDirty(0) + { } + + void beginSync() override; + void setPath(const QQuickPath *path) override; + void setStrokeColor(const QColor &color) override; + void setStrokeWidth(qreal w) override; + void setFillColor(const QColor &color) override; + void setFillRule(QQuickPathItem::FillRule fillRule) override; + void setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit) override; + void setCapStyle(QQuickPathItem::CapStyle capStyle) override; + void setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) override; + void setFillGradient(QQuickPathGradient *gradient) override; + void endSync() override; + void updatePathRenderNode() override; + + void setRootNode(QQuickPathItemGenericRootRenderNode *rn); + + struct Color4ub { unsigned char r, g, b, a; }; + +private: + void triangulateFill(); + void triangulateStroke(); + void updateFillNode(); + void updateStrokeNode(); + + QQuickItem *m_item; + QQuickPathItemGenericRootRenderNode *m_rootNode; + QTriangulatingStroker m_stroker; + QDashedStrokeProcessor m_dashStroker; + + QPen m_pen; + Color4ub m_strokeColor; + Color4ub m_fillColor; + Qt::FillRule m_fillRule; + QPainterPath m_path; + bool m_fillGradientActive; + QQuickPathItemGradientCache::GradientDesc m_fillGradient; + + QVector m_fillVertices; + QVector m_fillIndices; + QVector m_strokeVertices; + + int m_syncDirty; + int m_effectiveDirty; +}; + +class QQuickPathItemGenericRenderNode : public QSGGeometryNode +{ +public: + QQuickPathItemGenericRenderNode(QQuickWindow *window, QQuickPathItemGenericRootRenderNode *rootNode); + ~QQuickPathItemGenericRenderNode(); + + enum Material { + MatSolidColor, + MatLinearGradient + }; + + void activateMaterial(Material m); + + QQuickWindow *window() const { return m_window; } + QQuickPathItemGenericRootRenderNode *rootNode() const { return m_rootNode; } + + // shadow data for custom materials + QQuickPathItemGradientCache::GradientDesc m_fillGradient; + +private: + QSGGeometry m_geometry; + QQuickWindow *m_window; + QQuickPathItemGenericRootRenderNode *m_rootNode; + QSGMaterial *m_material; + QScopedPointer m_solidColorMaterial; + QScopedPointer m_linearGradientMaterial; + + friend class QQuickPathItemGenericRenderer; +}; + +class QQuickPathItemGenericRootRenderNode : public QSGNode +{ +public: + QQuickPathItemGenericRootRenderNode(QQuickWindow *window, bool hasFill, bool hasStroke); + ~QQuickPathItemGenericRootRenderNode(); + +private: + QQuickPathItemGenericRenderNode *m_fillNode; + QQuickPathItemGenericRenderNode *m_strokeNode; + + friend class QQuickPathItemGenericRenderer; +}; + +class QQuickPathItemGenericMaterialFactory +{ +public: + static QSGMaterial *createVertexColor(QQuickWindow *window); + static QSGMaterial *createLinearGradient(QQuickWindow *window, QQuickPathItemGenericRenderNode *node); +}; + +#ifndef QT_NO_OPENGL + +class QQuickPathItemLinearGradientShader : public QSGMaterialShader +{ +public: + QQuickPathItemLinearGradientShader(); + + void initialize() override; + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; + char const *const *attributeNames() const override; + + static QSGMaterialType type; + +private: + int m_opacityLoc; + int m_matrixLoc; + int m_gradStartLoc; + int m_gradEndLoc; +}; + +class QQuickPathItemLinearGradientMaterial : public QSGMaterial +{ +public: + QQuickPathItemLinearGradientMaterial(QQuickPathItemGenericRenderNode *node) + : m_node(node) + { + // Passing RequiresFullMatrix is essential in order to prevent the + // batch renderer from baking in simple, translate-only transforms into + // the vertex data. The shader will rely on the fact that + // vertexCoord.xy is the PathItem-space coordinate and so no modifications + // are welcome. + setFlag(Blending | RequiresFullMatrix); + } + + QSGMaterialType *type() const override + { + return &QQuickPathItemLinearGradientShader::type; + } + + int compare(const QSGMaterial *other) const override; + + QSGMaterialShader *createShader() const override + { + return new QQuickPathItemLinearGradientShader; + } + + QQuickPathItemGenericRenderNode *node() const { return m_node; } + +private: + QQuickPathItemGenericRenderNode *m_node; +}; + +#endif // QT_NO_OPENGL + +QT_END_NAMESPACE + +#endif // QQUICKPATHITEMGENERICRENDERER_P_H diff --git a/src/quick/items/qquickpathitemnvprrenderer.cpp b/src/quick/items/qquickpathitemnvprrenderer.cpp new file mode 100644 index 0000000000..d07a63c86d --- /dev/null +++ b/src/quick/items/qquickpathitemnvprrenderer.cpp @@ -0,0 +1,567 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickpathitemnvprrenderer_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +void QQuickPathItemNvprRenderer::beginSync() +{ + // nothing to do here +} + +void QQuickPathItemNvprRenderer::setPath(const QQuickPath *path) +{ + convertPath(path); + m_dirty |= DirtyPath; +} + +void QQuickPathItemNvprRenderer::setStrokeColor(const QColor &color) +{ + m_strokeColor = color; + m_dirty |= DirtyStyle; +} + +void QQuickPathItemNvprRenderer::setStrokeWidth(qreal w) +{ + m_strokeWidth = w; + m_dirty |= DirtyStyle; +} + +void QQuickPathItemNvprRenderer::setFillColor(const QColor &color) +{ + m_fillColor = color; + m_dirty |= DirtyStyle; +} + +void QQuickPathItemNvprRenderer::setFillRule(QQuickPathItem::FillRule fillRule) +{ + m_fillRule = fillRule; + m_dirty |= DirtyFillRule; +} + +void QQuickPathItemNvprRenderer::setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit) +{ + m_joinStyle = joinStyle; + m_miterLimit = miterLimit; + m_dirty |= DirtyStyle; +} + +void QQuickPathItemNvprRenderer::setCapStyle(QQuickPathItem::CapStyle capStyle) +{ + m_capStyle = capStyle; + m_dirty |= DirtyStyle; +} + +void QQuickPathItemNvprRenderer::setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) +{ + m_dashActive = strokeStyle == QQuickPathItem::DashLine; + m_dashOffset = dashOffset; + m_dashPattern = dashPattern; + m_dirty |= DirtyDash; +} + +void QQuickPathItemNvprRenderer::setFillGradient(QQuickPathGradient *gradient) +{ + m_fillGradientActive = gradient != nullptr; + if (gradient) { + m_fillGradient.stops = gradient->sortedGradientStops(); + m_fillGradient.spread = gradient->spread(); + if (QQuickPathLinearGradient *g = qobject_cast(gradient)) { + m_fillGradient.start = QPointF(g->x1(), g->y1()); + m_fillGradient.end = QPointF(g->x2(), g->y2()); + } else { + Q_UNREACHABLE(); + } + } + m_dirty |= DirtyFillGradient; +} + +void QQuickPathItemNvprRenderer::endSync() +{ + // nothing to do here +} + +void QQuickPathItemNvprRenderer::setNode(QQuickPathItemNvprRenderNode *node) +{ + if (m_node != node) { + m_node = node; + // Scenegraph nodes can be destroyed and then replaced by new ones over + // time; hence it is important to mark everything dirty for + // updatePathRenderNode(). We can assume the renderer has a full sync + // of the data at this point. + m_dirty = DirtyAll; + } +} + +QDebug operator<<(QDebug debug, const QQuickPathItemNvprRenderer::NvprPath &path) +{ + QDebugStateSaver saver(debug); + debug.space().noquote(); + debug << "Path with" << path.cmd.count() << "commands"; + int ci = 0; + for (GLubyte cmd : path.cmd) { + static struct { GLubyte cmd; const char *s; int coordCount; } nameTab[] = { + { GL_MOVE_TO_NV, "moveTo", 2 }, + { GL_LINE_TO_NV, "lineTo", 2 }, + { GL_QUADRATIC_CURVE_TO_NV, "quadTo", 4 }, + { GL_CUBIC_CURVE_TO_NV, "cubicTo", 6 }, + { GL_LARGE_CW_ARC_TO_NV, "arcTo-large-CW", 5 }, + { GL_LARGE_CCW_ARC_TO_NV, "arcTo-large-CCW", 5 }, + { GL_SMALL_CW_ARC_TO_NV, "arcTo-small-CW", 5 }, + { GL_SMALL_CCW_ARC_TO_NV, "arcTo-small-CCW", 5 }, + { GL_CLOSE_PATH_NV, "closePath", 0 } }; + for (size_t i = 0; i < sizeof(nameTab) / sizeof(nameTab[0]); ++i) { + if (nameTab[i].cmd == cmd) { + QByteArray cs; + for (int j = 0; j < nameTab[i].coordCount; ++j) { + cs.append(QByteArray::number(path.coord[ci++])); + cs.append(' '); + } + debug << "\n " << nameTab[i].s << " " << cs; + break; + } + } + } + return debug; +} + +static inline void appendCoords(QVector *v, QQuickCurve *c, QPointF *pos) +{ + QPointF p(c->hasRelativeX() ? pos->x() + c->relativeX() : c->x(), + c->hasRelativeY() ? pos->y() + c->relativeY() : c->y()); + v->append(p.x()); + v->append(p.y()); + *pos = p; +} + +static inline void appendControlCoords(QVector *v, QQuickPathQuad *c, const QPointF &pos) +{ + QPointF p(c->hasRelativeControlX() ? pos.x() + c->relativeControlX() : c->controlX(), + c->hasRelativeControlY() ? pos.y() + c->relativeControlY() : c->controlY()); + v->append(p.x()); + v->append(p.y()); +} + +static inline void appendControl1Coords(QVector *v, QQuickPathCubic *c, const QPointF &pos) +{ + QPointF p(c->hasRelativeControl1X() ? pos.x() + c->relativeControl1X() : c->control1X(), + c->hasRelativeControl1Y() ? pos.y() + c->relativeControl1Y() : c->control1Y()); + v->append(p.x()); + v->append(p.y()); +} + +static inline void appendControl2Coords(QVector *v, QQuickPathCubic *c, const QPointF &pos) +{ + QPointF p(c->hasRelativeControl2X() ? pos.x() + c->relativeControl2X() : c->control2X(), + c->hasRelativeControl2Y() ? pos.y() + c->relativeControl2Y() : c->control2Y()); + v->append(p.x()); + v->append(p.y()); +} + +void QQuickPathItemNvprRenderer::convertPath(const QQuickPath *path) +{ + m_path = NvprPath(); + if (!path) + return; + + const QList &pp(QQuickPathPrivate::get(path)->_pathElements); + if (pp.isEmpty()) + return; + + QPointF pos(path->startX(), path->startY()); + m_path.cmd.append(GL_MOVE_TO_NV); + m_path.coord.append(pos.x()); + m_path.coord.append(pos.y()); + + for (QQuickPathElement *e : pp) { + if (QQuickPathMove *o = qobject_cast(e)) { + m_path.cmd.append(GL_MOVE_TO_NV); + appendCoords(&m_path.coord, o, &pos); + } else if (QQuickPathLine *o = qobject_cast(e)) { + m_path.cmd.append(GL_LINE_TO_NV); + appendCoords(&m_path.coord, o, &pos); + } else if (QQuickPathQuad *o = qobject_cast(e)) { + m_path.cmd.append(GL_QUADRATIC_CURVE_TO_NV); + appendControlCoords(&m_path.coord, o, pos); + appendCoords(&m_path.coord, o, &pos); + } else if (QQuickPathCubic *o = qobject_cast(e)) { + m_path.cmd.append(GL_CUBIC_CURVE_TO_NV); + appendControl1Coords(&m_path.coord, o, pos); + appendControl2Coords(&m_path.coord, o, pos); + appendCoords(&m_path.coord, o, &pos); + } else if (QQuickPathArc *o = qobject_cast(e)) { + const bool sweepFlag = o->direction() == QQuickPathArc::Clockwise; // maps to CCW, not a typo + GLenum cmd; + if (o->useLargeArc()) + cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV; + else + cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV; + m_path.cmd.append(cmd); + m_path.coord.append(o->radiusX()); + m_path.coord.append(o->radiusY()); + m_path.coord.append(0.0f); // X axis rotation + appendCoords(&m_path.coord, o, &pos); + } else { + qWarning() << "PathItem/NVPR: unsupported Path element" << e; + } + } + + if (qFuzzyCompare(pos.x(), path->startX()) && qFuzzyCompare(pos.y(), path->startY())) + m_path.cmd.append(GL_CLOSE_PATH_NV); +} + +static inline QVector4D qsg_premultiply(const QColor &c, float globalOpacity) +{ + const float o = c.alphaF() * globalOpacity; + return QVector4D(c.redF() * o, c.greenF() * o, c.blueF() * o, o); +} + +void QQuickPathItemNvprRenderer::updatePathRenderNode() +{ + // Called on the render thread with gui blocked -> update the node with its + // own copy of all relevant data. + + if (!m_dirty) + return; + + // updatePathRenderNode() can be called several times with different dirty + // state before render() gets invoked. So accumulate. + m_node->m_dirty |= m_dirty; + + if (m_dirty & DirtyPath) + m_node->m_source = m_path; + + if (m_dirty & DirtyStyle) { + m_node->m_strokeWidth = m_strokeWidth; + m_node->m_strokeColor = qsg_premultiply(m_strokeColor, 1.0f); + m_node->m_fillColor = qsg_premultiply(m_fillColor, 1.0f); + switch (m_joinStyle) { + case QQuickPathItem::MiterJoin: + m_node->m_joinStyle = GL_MITER_TRUNCATE_NV; + break; + case QQuickPathItem::BevelJoin: + m_node->m_joinStyle = GL_BEVEL_NV; + break; + case QQuickPathItem::RoundJoin: + m_node->m_joinStyle = GL_ROUND_NV; + break; + default: + Q_UNREACHABLE(); + } + m_node->m_miterLimit = m_miterLimit; + switch (m_capStyle) { + case QQuickPathItem::FlatCap: + m_node->m_capStyle = GL_FLAT; + break; + case QQuickPathItem::SquareCap: + m_node->m_capStyle = GL_SQUARE_NV; + break; + case QQuickPathItem::RoundCap: + m_node->m_capStyle = GL_ROUND_NV; + break; + default: + Q_UNREACHABLE(); + } + } + + if (m_dirty & DirtyFillRule) { + switch (m_fillRule) { + case QQuickPathItem::OddEvenFill: + m_node->m_fillRule = GL_COUNT_UP_NV; + break; + case QQuickPathItem::WindingFill: + m_node->m_fillRule = GL_INVERT; + break; + default: + Q_UNREACHABLE(); + } + } + + if (m_dirty & DirtyDash) { + m_node->m_dashOffset = m_dashOffset; + if (m_dashActive) { + m_node->m_dashPattern.resize(m_dashPattern.count()); + // Multiply by strokeWidth because the PathItem API follows QPen + // meaning the input dash pattern here is in width units. + for (int i = 0; i < m_dashPattern.count(); ++i) + m_node->m_dashPattern[i] = GLfloat(m_dashPattern[i]) * m_strokeWidth; + } else { + m_node->m_dashPattern.clear(); + } + } + + if (m_dirty & DirtyFillGradient) { + m_node->m_fillGradientActive = m_fillGradientActive; + if (m_fillGradientActive) + m_node->m_fillGradient = m_fillGradient; + } + + m_node->markDirty(QSGNode::DirtyMaterial); + m_dirty = 0; +} + +bool QQuickPathItemNvprRenderNode::nvprInited = false; +QQuickNvprFunctions QQuickPathItemNvprRenderNode::nvpr; +QQuickNvprMaterialManager QQuickPathItemNvprRenderNode::mtlmgr; + +QQuickPathItemNvprRenderNode::QQuickPathItemNvprRenderNode(QQuickPathItem *item) + : m_item(item) +{ +} + +QQuickPathItemNvprRenderNode::~QQuickPathItemNvprRenderNode() +{ + releaseResources(); +} + +void QQuickPathItemNvprRenderNode::releaseResources() +{ + if (m_path) { + nvpr.deletePaths(m_path, 1); + m_path = 0; + } +} + +void QQuickNvprMaterialManager::create(QQuickNvprFunctions *nvpr) +{ + m_nvpr = nvpr; +} + +void QQuickNvprMaterialManager::releaseResources() +{ + QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); + for (MaterialDesc &mtl : m_materials) { + if (mtl.ppl) { + f->glDeleteProgramPipelines(1, &mtl.ppl); + mtl = MaterialDesc(); + } + } +} + +QQuickNvprMaterialManager::MaterialDesc *QQuickNvprMaterialManager::activateMaterial(Material m) +{ + QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); + MaterialDesc &mtl(m_materials[m]); + + if (!mtl.ppl) { + if (m == MatSolid) { + static const char *fragSrc = + "#version 310 es\n" + "precision highp float;\n" + "out vec4 fragColor;\n" + "uniform vec4 color;\n" + "uniform float opacity;\n" + "void main() {\n" + " fragColor = color * opacity;\n" + "}\n"; + if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { + qWarning("NVPR: Failed to create shader pipeline for solid fill"); + return nullptr; + } + Q_ASSERT(mtl.ppl && mtl.prg); + mtl.uniLoc[0] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "color"); + Q_ASSERT(mtl.uniLoc[0] >= 0); + mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); + Q_ASSERT(mtl.uniLoc[1] >= 0); + } else if (m == MatLinearGradient) { + static const char *fragSrc = + "#version 310 es\n" + "precision highp float;\n" + "layout(location = 0) in vec2 uv;" + "uniform float opacity;\n" + "uniform sampler2D gradTab;\n" + "uniform vec2 gradStart;\n" + "uniform vec2 gradEnd;\n" + "out vec4 fragColor;\n" + "void main() {\n" + " vec2 gradVec = gradEnd - gradStart;\n" + " float gradTabIndex = dot(gradVec, uv - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y);\n" + " fragColor = texture(gradTab, vec2(gradTabIndex, 0.5)) * opacity;\n" + "}\n"; + if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { + qWarning("NVPR: Failed to create shader pipeline for linear gradient"); + return nullptr; + } + Q_ASSERT(mtl.ppl && mtl.prg); + mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); + Q_ASSERT(mtl.uniLoc[1] >= 0); + mtl.uniLoc[2] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradStart"); + Q_ASSERT(mtl.uniLoc[2] >= 0); + mtl.uniLoc[3] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradEnd"); + Q_ASSERT(mtl.uniLoc[3] >= 0); + } else { + Q_UNREACHABLE(); + } + } + + f->glBindProgramPipeline(mtl.ppl); + + return &mtl; +} + +void QQuickPathItemNvprRenderNode::updatePath() +{ + if (m_dirty & QQuickPathItemNvprRenderer::DirtyPath) { + if (!m_path) { + m_path = nvpr.genPaths(1); + Q_ASSERT(m_path != 0); + } + nvpr.pathCommands(m_path, m_source.cmd.count(), m_source.cmd.constData(), + m_source.coord.count(), GL_FLOAT, m_source.coord.constData()); + } + + if (m_dirty & QQuickPathItemNvprRenderer::DirtyStyle) { + nvpr.pathParameterf(m_path, GL_PATH_STROKE_WIDTH_NV, m_strokeWidth); + nvpr.pathParameteri(m_path, GL_PATH_JOIN_STYLE_NV, m_joinStyle); + nvpr.pathParameteri(m_path, GL_PATH_MITER_LIMIT_NV, m_miterLimit); + nvpr.pathParameteri(m_path, GL_PATH_END_CAPS_NV, m_capStyle); + nvpr.pathParameteri(m_path, GL_PATH_DASH_CAPS_NV, m_capStyle); + } + + if (m_dirty & QQuickPathItemNvprRenderer::DirtyDash) { + nvpr.pathParameterf(m_path, GL_PATH_DASH_OFFSET_NV, m_dashOffset); + // count == 0 -> no dash + nvpr.pathDashArray(m_path, m_dashPattern.count(), m_dashPattern.constData()); + } +} + +void QQuickPathItemNvprRenderNode::render(const RenderState *state) +{ + if (!nvprInited) { + if (!nvpr.create()) { + qWarning("NVPR init failed"); + return; + } + mtlmgr.create(&nvpr); + nvprInited = true; + } + + updatePath(); + + QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); + f->glUseProgram(0); + QQuickNvprMaterialManager::MaterialDesc *mtl; + if (m_fillGradientActive) + mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatLinearGradient); + else + mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); + if (!mtl) + return; + + // Assume stencil buffer is cleared to 0 for each frame. + // Within the frame dppass=GL_ZERO for glStencilOp ensures stencil is reset and so no need to clear. + f->glStencilMask(~0); + f->glEnable(GL_STENCIL_TEST); + f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); + f->glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO); + + // Depth test against the opaque batches rendered before. + f->glEnable(GL_DEPTH_TEST); + f->glDepthFunc(GL_LESS); + nvpr.pathCoverDepthFunc(GL_LESS); + nvpr.pathStencilDepthOffset(-0.05f, -1); + + nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, matrix()->constData()); + nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, state->projectionMatrix()->constData()); + + if (state->scissorEnabled()) { + // scissor rect is already set, just enable scissoring + f->glEnable(GL_SCISSOR_TEST); + } + + if (!qFuzzyIsNull(m_fillColor.w()) || m_fillGradientActive) { + if (m_fillGradientActive) { + QSGTexture *tx = QQuickPathItemGradientCache::currentCache()->get(m_fillGradient); + tx->bind(); + // uv = vec2(coeff[0] * x + coeff[1] * y + coeff[2], coeff[3] * x + coeff[4] * y + coeff[5]) + // where x and y are in path coordinate space, which is just what + // we need since the gradient's start and stop are in that space too. + GLfloat coeff[6] = { 1, 0, 0, + 0, 1, 0 }; + nvpr.programPathFragmentInputGen(mtl->prg, 0, GL_OBJECT_LINEAR_NV, 2, coeff); + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[2], m_fillGradient.start.x(), m_fillGradient.start.y()); + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[3], m_fillGradient.end.x(), m_fillGradient.end.y()); + } else { + f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], + m_fillColor.x(), m_fillColor.y(), m_fillColor.z(), m_fillColor.w()); + } + f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); + nvpr.stencilThenCoverFillPath(m_path, m_fillRule, 0xFF, GL_BOUNDING_BOX_NV); + } + + if (!qFuzzyIsNull(m_strokeWidth) && !qFuzzyIsNull(m_strokeColor.w())) { + if (m_fillGradientActive) + mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); + f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], + m_strokeColor.x(), m_strokeColor.y(), m_strokeColor.z(), m_strokeColor.w()); + f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); + nvpr.stencilThenCoverStrokePath(m_path, 0x1, ~0, GL_CONVEX_HULL_NV); + } + + f->glBindProgramPipeline(0); + + m_dirty = 0; +} + +QSGRenderNode::StateFlags QQuickPathItemNvprRenderNode::changedStates() const +{ + return BlendState | StencilState | DepthState | ScissorState; +} + +QSGRenderNode::RenderingFlags QQuickPathItemNvprRenderNode::flags() const +{ + return DepthAwareRendering; // avoid hitting the less optimal no-opaque-batch path in the renderer +} + +QRectF QQuickPathItemNvprRenderNode::rect() const +{ + return QRect(0, 0, m_item->width(), m_item->height()); +} + +bool QQuickPathItemNvprRenderNode::isSupported() +{ + static const bool nvprDisabled = qEnvironmentVariableIntValue("QT_NO_NVPR") != 0; + return !nvprDisabled && QQuickNvprFunctions::isSupported(); +} + +QT_END_NAMESPACE diff --git a/src/quick/items/qquickpathitemnvprrenderer_p.h b/src/quick/items/qquickpathitemnvprrenderer_p.h new file mode 100644 index 0000000000..0075572324 --- /dev/null +++ b/src/quick/items/qquickpathitemnvprrenderer_p.h @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPATHITEMNVPRRENDERER_P_H +#define QQUICKPATHITEMNVPRRENDERER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickpathitem_p_p.h" +#include +#include +#include +#include +#include + +#ifndef QT_NO_OPENGL + +QT_BEGIN_NAMESPACE + +class QQuickPathItemNvprRenderNode; + +class QQuickPathItemNvprRenderer : public QQuickAbstractPathRenderer +{ +public: + enum Dirty { + DirtyPath = 0x01, + DirtyStyle = 0x02, + DirtyFillRule = 0x04, + DirtyDash = 0x08, + DirtyFillGradient = 0x10, + + DirtyAll = 0xFF + }; + + void beginSync() override; + void setPath(const QQuickPath *path) override; + void setStrokeColor(const QColor &color) override; + void setStrokeWidth(qreal w) override; + void setFillColor(const QColor &color) override; + void setFillRule(QQuickPathItem::FillRule fillRule) override; + void setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit) override; + void setCapStyle(QQuickPathItem::CapStyle capStyle) override; + void setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) override; + void setFillGradient(QQuickPathGradient *gradient) override; + void endSync() override; + void updatePathRenderNode() override; + + void setNode(QQuickPathItemNvprRenderNode *node); + + struct NvprPath { + QVector cmd; + QVector coord; + }; + +private: + void convertPath(const QQuickPath *path); + + QQuickPathItemNvprRenderNode *m_node = nullptr; + int m_dirty = 0; + + NvprPath m_path; + qreal m_strokeWidth; + QColor m_strokeColor; + QColor m_fillColor; + QQuickPathItem::JoinStyle m_joinStyle; + int m_miterLimit; + QQuickPathItem::CapStyle m_capStyle; + QQuickPathItem::FillRule m_fillRule; + bool m_dashActive; + qreal m_dashOffset; + QVector m_dashPattern; + bool m_fillGradientActive; + QQuickPathItemGradientCache::GradientDesc m_fillGradient; +}; + +QDebug operator<<(QDebug debug, const QQuickPathItemNvprRenderer::NvprPath &path); + +class QQuickNvprMaterialManager +{ +public: + enum Material { + MatSolid, + MatLinearGradient, + + NMaterials + }; + + struct MaterialDesc { + GLuint ppl = 0; + GLuint prg = 0; + int uniLoc[4]; + }; + + void create(QQuickNvprFunctions *nvpr); + MaterialDesc *activateMaterial(Material m); + void releaseResources(); + +private: + QQuickNvprFunctions *m_nvpr; + MaterialDesc m_materials[NMaterials]; +}; + +class QQuickPathItemNvprRenderNode : public QSGRenderNode +{ +public: + QQuickPathItemNvprRenderNode(QQuickPathItem *item); + ~QQuickPathItemNvprRenderNode(); + + void render(const RenderState *state) override; + void releaseResources() override; + StateFlags changedStates() const override; + RenderingFlags flags() const override; + QRectF rect() const override; + + static bool isSupported(); + +private: + void updatePath(); + + static bool nvprInited; + static QQuickNvprFunctions nvpr; + static QQuickNvprMaterialManager mtlmgr; + + QQuickPathItem *m_item; + GLuint m_path = 0; + int m_dirty = 0; + + QQuickPathItemNvprRenderer::NvprPath m_source; + GLfloat m_strokeWidth; + QVector4D m_strokeColor; + QVector4D m_fillColor; + GLenum m_joinStyle; + GLint m_miterLimit; + GLenum m_capStyle; + GLenum m_fillRule; + GLfloat m_dashOffset; + QVector m_dashPattern; + bool m_fillGradientActive; + QQuickPathItemGradientCache::GradientDesc m_fillGradient; + + friend class QQuickPathItemNvprRenderer; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_OPENGL + +#endif // QQUICKPATHITEMNVPRRENDERER_P_H diff --git a/src/quick/items/qquickpathitemsoftwarerenderer.cpp b/src/quick/items/qquickpathitemsoftwarerenderer.cpp new file mode 100644 index 0000000000..40732e4bf9 --- /dev/null +++ b/src/quick/items/qquickpathitemsoftwarerenderer.cpp @@ -0,0 +1,234 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickpathitemsoftwarerenderer_p.h" +#include + +QT_BEGIN_NAMESPACE + +void QQuickPathItemSoftwareRenderer::beginSync() +{ + // nothing to do here +} + +void QQuickPathItemSoftwareRenderer::setPath(const QQuickPath *path) +{ + m_path = path ? path->path() : QPainterPath(); + m_dirty |= DirtyPath; +} + +void QQuickPathItemSoftwareRenderer::setStrokeColor(const QColor &color) +{ + m_pen.setColor(color); + m_dirty |= DirtyPen; +} + +void QQuickPathItemSoftwareRenderer::setStrokeWidth(qreal w) +{ + m_pen.setWidthF(w); + m_dirty |= DirtyPen; +} + +void QQuickPathItemSoftwareRenderer::setFillColor(const QColor &color) +{ + m_fillColor = color; + m_brush.setColor(m_fillColor); + m_dirty |= DirtyBrush; +} + +void QQuickPathItemSoftwareRenderer::setFillRule(QQuickPathItem::FillRule fillRule) +{ + m_fillRule = Qt::FillRule(fillRule); + m_dirty |= DirtyFillRule; +} + +void QQuickPathItemSoftwareRenderer::setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit) +{ + m_pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); + m_pen.setMiterLimit(miterLimit); + m_dirty |= DirtyPen; +} + +void QQuickPathItemSoftwareRenderer::setCapStyle(QQuickPathItem::CapStyle capStyle) +{ + m_pen.setCapStyle(Qt::PenCapStyle(capStyle)); + m_dirty |= DirtyPen; +} + +void QQuickPathItemSoftwareRenderer::setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) +{ + switch (strokeStyle) { + case QQuickPathItem::SolidLine: + m_pen.setStyle(Qt::SolidLine); + break; + case QQuickPathItem::DashLine: + m_pen.setStyle(Qt::CustomDashLine); + m_pen.setDashPattern(dashPattern); + m_pen.setDashOffset(dashOffset); + break; + default: + break; + } + m_dirty |= DirtyPen; +} + +void QQuickPathItemSoftwareRenderer::setFillGradient(QQuickPathGradient *gradient) +{ + if (QQuickPathLinearGradient *linearGradient = qobject_cast(gradient)) { + QLinearGradient painterGradient(linearGradient->x1(), linearGradient->y1(), + linearGradient->x2(), linearGradient->y2()); + painterGradient.setStops(linearGradient->sortedGradientStops()); + switch (gradient->spread()) { + case QQuickPathGradient::PadSpread: + painterGradient.setSpread(QGradient::PadSpread); + break; + case QQuickPathGradient::RepeatSpread: + painterGradient.setSpread(QGradient::RepeatSpread); + break; + case QQuickPathGradient::ReflectSpread: + painterGradient.setSpread(QGradient::ReflectSpread); + break; + default: + break; + } + m_brush = QBrush(painterGradient); + } else { + m_brush = QBrush(m_fillColor); + } + m_dirty |= DirtyBrush; +} + +void QQuickPathItemSoftwareRenderer::endSync() +{ + // nothing to do here +} + +void QQuickPathItemSoftwareRenderer::setNode(QQuickPathItemSoftwareRenderNode *node) +{ + if (m_node != node) { + m_node = node; + // Scenegraph nodes can be destroyed and then replaced by new ones over + // time; hence it is important to mark everything dirty for + // updatePathRenderNode(). We can assume the renderer has a full sync + // of the data at this point. + m_dirty = DirtyAll; + } +} + +void QQuickPathItemSoftwareRenderer::updatePathRenderNode() +{ + if (!m_dirty) + return; + + // updatePathRenderNode() can be called several times with different dirty + // state before render() gets invoked. So accumulate. + m_node->m_dirty |= m_dirty; + + if (m_dirty & DirtyPath) { + m_node->m_path = m_path; + m_node->m_path.setFillRule(m_fillRule); + } + + if (m_dirty & DirtyFillRule) + m_node->m_path.setFillRule(m_fillRule); + + if (m_dirty & DirtyPen) + m_node->m_pen = m_pen; + + if (m_dirty & DirtyBrush) + m_node->m_brush = m_brush; + + m_node->markDirty(QSGNode::DirtyMaterial); + m_dirty = 0; +} + +QQuickPathItemSoftwareRenderNode::QQuickPathItemSoftwareRenderNode(QQuickPathItem *item) + : m_item(item) +{ +} + +QQuickPathItemSoftwareRenderNode::~QQuickPathItemSoftwareRenderNode() +{ + releaseResources(); +} + +void QQuickPathItemSoftwareRenderNode::releaseResources() +{ +} + +void QQuickPathItemSoftwareRenderNode::render(const RenderState *state) +{ + if (m_path.isEmpty()) + return; + + QSGRendererInterface *rif = m_item->window()->rendererInterface(); + QPainter *p = static_cast(rif->getResource(m_item->window(), QSGRendererInterface::PainterResource)); + Q_ASSERT(p); + + p->setTransform(matrix()->toTransform()); + p->setOpacity(inheritedOpacity()); + + const QRegion *clipRegion = state->clipRegion(); + if (clipRegion && !clipRegion->isEmpty()) + p->setClipRegion(*clipRegion, Qt::IntersectClip); + + p->setPen(!qFuzzyIsNull(m_pen.widthF()) && m_pen.color() != Qt::transparent ? m_pen : Qt::NoPen); + p->setBrush(m_brush.color() != Qt::transparent ? m_brush : Qt::NoBrush); + p->drawPath(m_path); + + m_dirty = 0; +} + +QSGRenderNode::StateFlags QQuickPathItemSoftwareRenderNode::changedStates() const +{ + return 0; +} + +QSGRenderNode::RenderingFlags QQuickPathItemSoftwareRenderNode::flags() const +{ + return BoundedRectRendering; // avoid fullscreen updates by saying we won't draw outside rect() +} + +QRectF QQuickPathItemSoftwareRenderNode::rect() const +{ + return QRect(0, 0, m_item->width(), m_item->height()); +} + +QT_END_NAMESPACE diff --git a/src/quick/items/qquickpathitemsoftwarerenderer_p.h b/src/quick/items/qquickpathitemsoftwarerenderer_p.h new file mode 100644 index 0000000000..584771425d --- /dev/null +++ b/src/quick/items/qquickpathitemsoftwarerenderer_p.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPATHITEMSOFTWARERENDERER_P_H +#define QQUICKPATHITEMSOFTWARERENDERER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickpathitem_p_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickPathItemSoftwareRenderNode; + +class QQuickPathItemSoftwareRenderer : public QQuickAbstractPathRenderer +{ +public: + enum Dirty { + DirtyPath = 0x01, + DirtyPen = 0x02, + DirtyFillRule = 0x04, + DirtyBrush = 0x08, + + DirtyAll = 0xFF + }; + + void beginSync() override; + void setPath(const QQuickPath *path) override; + void setStrokeColor(const QColor &color) override; + void setStrokeWidth(qreal w) override; + void setFillColor(const QColor &color) override; + void setFillRule(QQuickPathItem::FillRule fillRule) override; + void setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit) override; + void setCapStyle(QQuickPathItem::CapStyle capStyle) override; + void setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) override; + void setFillGradient(QQuickPathGradient *gradient) override; + void endSync() override; + void updatePathRenderNode() override; + + void setNode(QQuickPathItemSoftwareRenderNode *node); + +private: + QQuickPathItemSoftwareRenderNode *m_node = nullptr; + int m_dirty = 0; + + QPainterPath m_path; + QPen m_pen; + QColor m_fillColor; + QBrush m_brush; + Qt::FillRule m_fillRule; +}; + +class QQuickPathItemSoftwareRenderNode : public QSGRenderNode +{ +public: + QQuickPathItemSoftwareRenderNode(QQuickPathItem *item); + ~QQuickPathItemSoftwareRenderNode(); + + void render(const RenderState *state) override; + void releaseResources() override; + StateFlags changedStates() const override; + RenderingFlags flags() const override; + QRectF rect() const override; + +private: + QQuickPathItem *m_item; + int m_dirty = 0; + + QPainterPath m_path; + QPen m_pen; + QBrush m_brush; + + friend class QQuickPathItemSoftwareRenderer; +}; + +QT_END_NAMESPACE + +#endif // QQUICKPATHITEMSOFTWARERENDERER_P_H diff --git a/src/quick/items/shaders/lineargradient.frag b/src/quick/items/shaders/lineargradient.frag new file mode 100644 index 0000000000..7f4a739109 --- /dev/null +++ b/src/quick/items/shaders/lineargradient.frag @@ -0,0 +1,9 @@ +uniform sampler2D gradTabTexture; +uniform highp float opacity; + +varying highp float gradTabIndex; + +void main() +{ + gl_FragColor = texture2D(gradTabTexture, vec2(gradTabIndex, 0.5)) * opacity; +} diff --git a/src/quick/items/shaders/lineargradient.vert b/src/quick/items/shaders/lineargradient.vert new file mode 100644 index 0000000000..eb21b8886b --- /dev/null +++ b/src/quick/items/shaders/lineargradient.vert @@ -0,0 +1,15 @@ +attribute vec4 vertexCoord; +attribute vec4 vertexColor; + +uniform mat4 matrix; +uniform vec2 gradStart; +uniform vec2 gradEnd; + +varying float gradTabIndex; + +void main() +{ + vec2 gradVec = gradEnd - gradStart; + gradTabIndex = dot(gradVec, vertexCoord.xy - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y); + gl_Position = matrix * vertexCoord; +} diff --git a/src/quick/items/shaders/lineargradient_core.frag b/src/quick/items/shaders/lineargradient_core.frag new file mode 100644 index 0000000000..5908acfa67 --- /dev/null +++ b/src/quick/items/shaders/lineargradient_core.frag @@ -0,0 +1,12 @@ +#version 150 core + +uniform sampler2D gradTabTexture; +uniform float opacity; + +in float gradTabIndex; +out vec4 fragColor; + +void main() +{ + fragColor = texture(gradTabTexture, vec2(gradTabIndex, 0.5)) * opacity; +} diff --git a/src/quick/items/shaders/lineargradient_core.vert b/src/quick/items/shaders/lineargradient_core.vert new file mode 100644 index 0000000000..60b56f38e3 --- /dev/null +++ b/src/quick/items/shaders/lineargradient_core.vert @@ -0,0 +1,17 @@ +#version 150 core + +in vec4 vertexCoord; +in vec4 vertexColor; + +uniform mat4 matrix; +uniform vec2 gradStart; +uniform vec2 gradEnd; + +out float gradTabIndex; + +void main() +{ + vec2 gradVec = gradEnd - gradStart; + gradTabIndex = dot(gradVec, vertexCoord.xy - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y); + gl_Position = matrix * vertexCoord; +} diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index 8de205fc98..b8ed190b33 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -978,6 +978,10 @@ void Renderer::nodeChangedBatchRoot(Node *node, Node *root) e->root = root; e->boundsComputed = false; } + } else if (node->type() == QSGNode::RenderNodeType) { + RenderNodeElement *e = node->renderNodeElement(); + if (e) + e->root = root; } SHADOWNODE_TRAVERSE(node) diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.cpp b/src/quick/scenegraph/coreapi/qsgrendernode.cpp index 5915d51f2b..1bc0210b72 100644 --- a/src/quick/scenegraph/coreapi/qsgrendernode.cpp +++ b/src/quick/scenegraph/coreapi/qsgrendernode.cpp @@ -160,10 +160,7 @@ QSGRenderNode::StateFlags QSGRenderNode::changedStates() const \list \li glDepthMask(false) \li glDisable(GL_DEPTH_TEST) - \li glStencilMask(0) - \li glEnable(GL_STENCIL_TEST)/glDisable(GL_STENCIL_TEST) depending on clip \li glStencilFunc(GL_EQUAL, state.stencilValue, 0xff) depending on clip - \li glEnable(GL_SCISSOR_TEST)/glDisable(GL_SCISSOR_TEST) depending on clip \li glScissor(state.scissorRect.x(), state.scissorRect.y(), state.scissorRect.width(), state.scissorRect.height()) depending on clip \li glEnable(GL_BLEND) diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp index 2c5b4ff5c8..aaf4635c0c 100644 --- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp +++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp @@ -260,7 +260,7 @@ void QSGDefaultRenderContext::compileShader(QSGMaterialShader *shader, QSGMateri if (vertexCode || fragmentCode) { Q_ASSERT_X((material->flags() & QSGMaterial::CustomCompileStep) == 0, "QSGRenderContext::compile()", - "materials with custom compile step cannot have custom vertex/fragment code"); + "materials with custom compile step cannot have modified vertex or fragment code"); QOpenGLShaderProgram *p = shader->program(); p->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexCode ? vertexCode : shader->vertexShader()); p->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragmentCode ? fragmentCode : shader->fragmentShader()); diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp index 47248f2f37..d761eac62f 100644 --- a/src/quick/scenegraph/util/qsgtexture.cpp +++ b/src/quick/scenegraph/util/qsgtexture.cpp @@ -251,11 +251,15 @@ static void qt_debug_remove_texture(QSGTexture* texture) Specifies how the texture should treat texture coordinates. - \value Repeat Only the factional part of the texture coordiante is + \value Repeat Only the fractional part of the texture coordinate is used, causing values above 1 and below 0 to repeat. \value ClampToEdge Values above 1 are clamped to 1 and values below 0 are clamped to 0. + + \value MirroredRepeat When the texture coordinate is even, only the + fractional part is used. When odd, the texture coordinate is set to + \c{1 - fractional part}. This value has been introduced in Qt 5.10. */ /*! @@ -550,7 +554,9 @@ void QSGTexture::updateBindOptions(bool force) if (force || d->wrapChanged) { #ifndef QT_NO_DEBUG - if (d->horizontalWrap == Repeat || d->verticalWrap == Repeat) { + if (d->horizontalWrap == Repeat || d->verticalWrap == Repeat + || d->horizontalWrap == MirroredRepeat || d->verticalWrap == MirroredRepeat) + { bool npotSupported = QOpenGLFunctions(QOpenGLContext::currentContext()).hasOpenGLFeature(QOpenGLFunctions::NPOTTextures); QSize size = textureSize(); bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height()); @@ -558,8 +564,18 @@ void QSGTexture::updateBindOptions(bool force) qWarning("Scene Graph: This system does not support the REPEAT wrap mode for non-power-of-two textures."); } #endif - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, d->horizontalWrap == Repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, d->verticalWrap == Repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE); + GLenum wrapS = GL_CLAMP_TO_EDGE; + if (d->horizontalWrap == Repeat) + wrapS = GL_REPEAT; + else if (d->horizontalWrap == MirroredRepeat) + wrapS = GL_MIRRORED_REPEAT; + GLenum wrapT = GL_CLAMP_TO_EDGE; + if (d->verticalWrap == Repeat) + wrapT = GL_REPEAT; + else if (d->verticalWrap == MirroredRepeat) + wrapT = GL_MIRRORED_REPEAT; + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT); d->wrapChanged = false; } #else diff --git a/src/quick/scenegraph/util/qsgtexture.h b/src/quick/scenegraph/util/qsgtexture.h index f0509b58ae..2b29525efd 100644 --- a/src/quick/scenegraph/util/qsgtexture.h +++ b/src/quick/scenegraph/util/qsgtexture.h @@ -58,7 +58,8 @@ public: enum WrapMode { Repeat, - ClampToEdge + ClampToEdge, + MirroredRepeat }; enum Filtering { diff --git a/src/quick/scenegraph/util/qsgtexture_p.h b/src/quick/scenegraph/util/qsgtexture_p.h index b6fcfc31c4..3a10d85b4d 100644 --- a/src/quick/scenegraph/util/qsgtexture_p.h +++ b/src/quick/scenegraph/util/qsgtexture_p.h @@ -70,8 +70,8 @@ public: uint wrapChanged : 1; uint filteringChanged : 1; - uint horizontalWrap : 1; - uint verticalWrap : 1; + uint horizontalWrap : 2; + uint verticalWrap : 2; uint mipmapMode : 2; uint filterMode : 2; }; diff --git a/src/quick/scenegraph/util/qsgtexturematerial.cpp b/src/quick/scenegraph/util/qsgtexturematerial.cpp index 9326ea640d..fcafbba6a2 100644 --- a/src/quick/scenegraph/util/qsgtexturematerial.cpp +++ b/src/quick/scenegraph/util/qsgtexturematerial.cpp @@ -278,7 +278,7 @@ void QSGOpaqueTextureMaterial::setTexture(QSGTexture *texture) Returns this material's horizontal wrap mode. - The default horizontal wrap mode is \c QSGTexutre::ClampToEdge. + The default horizontal wrap mode is \c QSGTexture::ClampToEdge. */ @@ -299,7 +299,7 @@ void QSGOpaqueTextureMaterial::setTexture(QSGTexture *texture) Returns this material's vertical wrap mode. - The default vertical wrap mode is \c QSGTexutre::ClampToEdge. + The default vertical wrap mode is \c QSGTexture::ClampToEdge. */ diff --git a/src/quick/util/qquicknvprfunctions.cpp b/src/quick/util/qquicknvprfunctions.cpp new file mode 100644 index 0000000000..40eb2bb932 --- /dev/null +++ b/src/quick/util/qquicknvprfunctions.cpp @@ -0,0 +1,284 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicknvprfunctions_p.h" + +#ifndef QT_NO_OPENGL + +#include +#include +#include +#include "qquicknvprfunctions_p_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QQuickNvprFunctions + + \brief Function resolvers and other helpers for GL_NV_path_rendering + for both desktop (GL 4.3+) and mobile/embedded (GLES 3.1+) in a manner + that does not distract builds that do not have NVPR support either at + compile or run time. + + \internal + */ + +QQuickNvprFunctions::QQuickNvprFunctions() + : d(new QQuickNvprFunctionsPrivate(this)) +{ +} + +QQuickNvprFunctions::~QQuickNvprFunctions() +{ + delete d; +} + +/*! + \return a recommended QSurfaceFormat suitable for GL_NV_path_rendering on top + of OpenGL 4.3 or OpenGL ES 3.1. + */ +QSurfaceFormat QQuickNvprFunctions::format() +{ + QSurfaceFormat fmt; + fmt.setDepthBufferSize(24); + fmt.setStencilBufferSize(8); + if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { + fmt.setVersion(4, 3); + fmt.setProfile(QSurfaceFormat::CompatibilityProfile); + } else if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { + fmt.setVersion(3, 1); + } + return fmt; +} + +/*! + \return true if GL_NV_path_rendering is supported with the current OpenGL + context. + + When there is no current context, a temporary dummy one will be created and + made current. + */ +bool QQuickNvprFunctions::isSupported() +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + QScopedPointer tempContext; + QScopedPointer tempSurface; + if (!ctx) { + tempContext.reset(new QOpenGLContext); + if (!tempContext->create()) + return false; + ctx = tempContext.data(); + tempSurface.reset(new QOffscreenSurface); + tempSurface->setFormat(ctx->format()); + tempSurface->create(); + if (!ctx->makeCurrent(tempSurface.data())) + return false; + } + + if (!ctx->hasExtension(QByteArrayLiteral("GL_NV_path_rendering"))) + return false; + + // Do not check for DSA as the string may not be exposed on ES + // drivers, yet the functions we need are resolvable. +#if 0 + if (!ctx->hasExtension(QByteArrayLiteral("GL_EXT_direct_state_access"))) { + qWarning("QtQuickPath/NVPR: GL_EXT_direct_state_access not supported"); + return false; + } +#endif + + return true; +} + +/*! + Initializes using the current OpenGL context. + + \return true when GL_NV_path_rendering is supported and initialization was + successful. + */ +bool QQuickNvprFunctions::create() +{ + return isSupported() && d->resolve(); +} + +/*! + Creates a program pipeline consisting of a separable fragment shader program. + + This is essential for using NVPR with OpenGL ES 3.1+ since normal, + GLES2-style programs would not work without a vertex shader. + + \note \a fragmentShaderSource should be a \c{version 310 es} shader since + this works both on desktop and embedded NVIDIA drivers, thus avoiding the + need to fight GLSL and GLSL ES differences. + + The pipeline object is stored into \a pipeline, the fragment shader program + into \a program. + + Use QOpenGLExtraFunctions to set uniforms, bind the pipeline, etc. + + \return \c false on failure in which case the error log is printed on the + debug output. \c true on success. + */ +bool QQuickNvprFunctions::createFragmentOnlyPipeline(const char *fragmentShaderSource, GLuint *pipeline, GLuint *program) +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (!ctx) + return false; + + QOpenGLExtraFunctions *f = ctx->extraFunctions(); + *program = f->glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &fragmentShaderSource); + GLint status = 0; + f->glGetProgramiv(*program, GL_LINK_STATUS, &status); + if (!status) { + GLint len = 0; + f->glGetProgramiv(*program, GL_INFO_LOG_LENGTH, &len); + if (len) { + QByteArray s; + s.resize(len); + f->glGetProgramInfoLog(*program, s.count(), nullptr, s.data()); + qWarning("Failed to create separable shader program:\n%s", s.constData()); + } + return false; + } + + f->glGenProgramPipelines(1, pipeline); + f->glUseProgramStages(*pipeline, GL_FRAGMENT_SHADER_BIT, *program); + f->glActiveShaderProgram(*pipeline, *program); + + f->glValidateProgramPipeline(*pipeline); + status = 0; + f->glGetProgramPipelineiv(*pipeline, GL_VALIDATE_STATUS, &status); + if (!status) { + GLint len = 0; + f->glGetProgramPipelineiv(*pipeline, GL_INFO_LOG_LENGTH, &len); + if (len) { + QByteArray s; + s.resize(len); + f->glGetProgramPipelineInfoLog(*pipeline, s.count(), nullptr, s.data()); + qWarning("Program pipeline validation failed:\n%s", s.constData()); + } + return false; + } + + return true; +} + +#define PROC(type, name) reinterpret_cast(ctx->getProcAddress(#name)) + +bool QQuickNvprFunctionsPrivate::resolve() +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + + q->genPaths = PROC(PFNGLGENPATHSNVPROC, glGenPathsNV); + q->deletePaths = PROC(PFNGLDELETEPATHSNVPROC, glDeletePathsNV); + q->isPath = PROC(PFNGLISPATHNVPROC, glIsPathNV); + q->pathCommands = PROC(PFNGLPATHCOMMANDSNVPROC, glPathCommandsNV); + q->pathCoords = PROC(PFNGLPATHCOORDSNVPROC, glPathCoordsNV); + q->pathSubCommands = PROC(PFNGLPATHSUBCOMMANDSNVPROC, glPathSubCommandsNV); + q->pathSubCoords = PROC(PFNGLPATHSUBCOORDSNVPROC, glPathSubCoordsNV); + q->pathString = PROC(PFNGLPATHSTRINGNVPROC, glPathStringNV); + q->pathGlyphs = PROC(PFNGLPATHGLYPHSNVPROC, glPathGlyphsNV); + q->pathGlyphRange = PROC(PFNGLPATHGLYPHRANGENVPROC, glPathGlyphRangeNV); + q->weightPaths = PROC(PFNGLWEIGHTPATHSNVPROC, glWeightPathsNV); + q->copyPath = PROC(PFNGLCOPYPATHNVPROC, glCopyPathNV); + q->interpolatePaths = PROC(PFNGLINTERPOLATEPATHSNVPROC, glInterpolatePathsNV); + q->transformPath = PROC(PFNGLTRANSFORMPATHNVPROC, glTransformPathNV); + q->pathParameteriv = PROC(PFNGLPATHPARAMETERIVNVPROC, glPathParameterivNV); + q->pathParameteri = PROC(PFNGLPATHPARAMETERINVPROC, glPathParameteriNV); + q->pathParameterfv = PROC(PFNGLPATHPARAMETERFVNVPROC, glPathParameterfvNV); + q->pathParameterf = PROC(PFNGLPATHPARAMETERFNVPROC, glPathParameterfNV); + q->pathDashArray = PROC(PFNGLPATHDASHARRAYNVPROC, glPathDashArrayNV); + q->pathStencilFunc = PROC(PFNGLPATHSTENCILFUNCNVPROC, glPathStencilFuncNV); + q->pathStencilDepthOffset = PROC(PFNGLPATHSTENCILDEPTHOFFSETNVPROC, glPathStencilDepthOffsetNV); + q->stencilFillPath = PROC(PFNGLSTENCILFILLPATHNVPROC, glStencilFillPathNV); + q->stencilStrokePath = PROC(PFNGLSTENCILSTROKEPATHNVPROC, glStencilStrokePathNV); + q->stencilFillPathInstanced = PROC(PFNGLSTENCILFILLPATHINSTANCEDNVPROC, glStencilFillPathInstancedNV); + q->stencilStrokePathInstanced = PROC(PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC, glStencilStrokePathInstancedNV); + q->pathCoverDepthFunc = PROC(PFNGLPATHCOVERDEPTHFUNCNVPROC, glPathCoverDepthFuncNV); + q->pathColorGen = PROC(PFNGLPATHCOLORGENNVPROC, glPathColorGenNV); + q->pathTexGen = PROC(PFNGLPATHTEXGENNVPROC, glPathTexGenNV); + q->pathFogGen = PROC(PFNGLPATHFOGGENNVPROC, glPathFogGenNV); + q->coverFillPath = PROC(PFNGLCOVERFILLPATHNVPROC, glCoverFillPathNV); + q->coverStrokePath = PROC(PFNGLCOVERSTROKEPATHNVPROC, glCoverStrokePathNV); + q->coverFillPathInstanced = PROC(PFNGLCOVERFILLPATHINSTANCEDNVPROC, glCoverFillPathInstancedNV); + q->coverStrokePathInstanced = PROC(PFNGLCOVERSTROKEPATHINSTANCEDNVPROC, glCoverStrokePathInstancedNV); + q->getPathParameteriv = PROC(PFNGLGETPATHPARAMETERIVNVPROC, glGetPathParameterivNV); + q->getPathParameterfv = PROC(PFNGLGETPATHPARAMETERFVNVPROC, glGetPathParameterfvNV); + q->getPathCommands = PROC(PFNGLGETPATHCOMMANDSNVPROC, glGetPathCommandsNV); + q->getPathCoords = PROC(PFNGLGETPATHCOORDSNVPROC, glGetPathCoordsNV); + q->getPathDashArray = PROC(PFNGLGETPATHDASHARRAYNVPROC, glGetPathDashArrayNV); + q->getPathMetrics = PROC(PFNGLGETPATHMETRICSNVPROC, glGetPathMetricsNV); + q->getPathMetricRange = PROC(PFNGLGETPATHMETRICRANGENVPROC, glGetPathMetricRangeNV); + q->getPathSpacing = PROC(PFNGLGETPATHSPACINGNVPROC, glGetPathSpacingNV); + q->getPathColorgeniv = PROC(PFNGLGETPATHCOLORGENIVNVPROC, glGetPathColorGenivNV); + q->getPathColorgenfv = PROC(PFNGLGETPATHCOLORGENFVNVPROC, glGetPathColorGenfvNV); + q->getPathTexGeniv = PROC(PFNGLGETPATHTEXGENIVNVPROC, glGetPathTexGenivNV); + q->getPathTexGenfv = PROC(PFNGLGETPATHTEXGENFVNVPROC, glGetPathTexGenfvNV); + q->isPointInFillPath = PROC(PFNGLISPOINTINFILLPATHNVPROC, glIsPointInFillPathNV); + q->isPointInStrokePath = PROC(PFNGLISPOINTINSTROKEPATHNVPROC, glIsPointInStrokePathNV); + q->getPathLength = PROC(PFNGLGETPATHLENGTHNVPROC, glGetPathLengthNV); + q->getPointAlongPath = PROC(PFNGLPOINTALONGPATHNVPROC, glPointAlongPathNV); + q->matrixLoad3x2f = PROC(PFNGLMATRIXLOAD3X2FNVPROC, glMatrixLoad3x2fNV); + q->matrixLoad3x3f = PROC(PFNGLMATRIXLOAD3X3FNVPROC, glMatrixLoad3x3fNV); + q->matrixLoadTranspose3x3f = PROC(PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC, glMatrixLoadTranspose3x3fNV); + q->matrixMult3x2f = PROC(PFNGLMATRIXMULT3X2FNVPROC, glMatrixMult3x2fNV); + q->matrixMult3x3f = PROC(PFNGLMATRIXMULT3X3FNVPROC, glMatrixMult3x3fNV); + q->matrixMultTranspose3x3f = PROC(PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC, glMatrixMultTranspose3x3fNV); + q->stencilThenCoverFillPath = PROC(PFNGLSTENCILTHENCOVERFILLPATHNVPROC, glStencilThenCoverFillPathNV); + q->stencilThenCoverStrokePath = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC, glStencilThenCoverStrokePathNV); + q->stencilThenCoverFillPathInstanced = PROC(PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC, glStencilThenCoverFillPathInstancedNV); + q->stencilThenCoverStrokePathInstanced = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC, glStencilThenCoverStrokePathInstancedNV); + q->pathGlyphIndexRange = PROC(PFNGLPATHGLYPHINDEXRANGENVPROC, glPathGlyphIndexRangeNV); + q->pathGlyphIndexArray = PROC(PFNGLPATHGLYPHINDEXARRAYNVPROC, glPathGlyphIndexArrayNV); + q->pathMemoryGlyphIndexArray = PROC(PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC, glPathMemoryGlyphIndexArrayNV); + q->programPathFragmentInputGen = PROC(PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC, glProgramPathFragmentInputGenNV); + q->getProgramResourcefv = PROC(PFNGLGETPROGRAMRESOURCEFVNVPROC, glGetProgramResourcefvNV); + + q->matrixLoadf = PROC(PFNGLMATRIXLOADFEXTPROC, glMatrixLoadfEXT); + q->matrixLoadIdentity = PROC(PFNGLMATRIXLOADIDENTITYEXTPROC, glMatrixLoadIdentityEXT); + + return q->genPaths != nullptr // base path rendering ext + && q->programPathFragmentInputGen != nullptr // updated path rendering ext + && q->matrixLoadf != nullptr // direct state access ext + && q->matrixLoadIdentity != nullptr; +} + +QT_END_NAMESPACE + +#endif // QT_NO_OPENGL diff --git a/src/quick/util/qquicknvprfunctions_p.h b/src/quick/util/qquicknvprfunctions_p.h new file mode 100644 index 0000000000..7900388305 --- /dev/null +++ b/src/quick/util/qquicknvprfunctions_p.h @@ -0,0 +1,406 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKNVPRFUNCTIONS_P_H +#define QQUICKNVPRFUNCTIONS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#ifndef QT_NO_OPENGL + +QT_BEGIN_NAMESPACE + +#ifndef GL_NV_path_rendering +#define GL_PATH_FORMAT_SVG_NV 0x9070 +#define GL_PATH_FORMAT_PS_NV 0x9071 +#define GL_STANDARD_FONT_NAME_NV 0x9072 +#define GL_SYSTEM_FONT_NAME_NV 0x9073 +#define GL_FILE_NAME_NV 0x9074 +#define GL_PATH_STROKE_WIDTH_NV 0x9075 +#define GL_PATH_END_CAPS_NV 0x9076 +#define GL_PATH_INITIAL_END_CAP_NV 0x9077 +#define GL_PATH_TERMINAL_END_CAP_NV 0x9078 +#define GL_PATH_JOIN_STYLE_NV 0x9079 +#define GL_PATH_MITER_LIMIT_NV 0x907A +#define GL_PATH_DASH_CAPS_NV 0x907B +#define GL_PATH_INITIAL_DASH_CAP_NV 0x907C +#define GL_PATH_TERMINAL_DASH_CAP_NV 0x907D +#define GL_PATH_DASH_OFFSET_NV 0x907E +#define GL_PATH_CLIENT_LENGTH_NV 0x907F +#define GL_PATH_FILL_MODE_NV 0x9080 +#define GL_PATH_FILL_MASK_NV 0x9081 +#define GL_PATH_FILL_COVER_MODE_NV 0x9082 +#define GL_PATH_STROKE_COVER_MODE_NV 0x9083 +#define GL_PATH_STROKE_MASK_NV 0x9084 +#define GL_COUNT_UP_NV 0x9088 +#define GL_COUNT_DOWN_NV 0x9089 +#define GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A +#define GL_CONVEX_HULL_NV 0x908B +#define GL_BOUNDING_BOX_NV 0x908D +#define GL_TRANSLATE_X_NV 0x908E +#define GL_TRANSLATE_Y_NV 0x908F +#define GL_TRANSLATE_2D_NV 0x9090 +#define GL_TRANSLATE_3D_NV 0x9091 +#define GL_AFFINE_2D_NV 0x9092 +#define GL_AFFINE_3D_NV 0x9094 +#define GL_TRANSPOSE_AFFINE_2D_NV 0x9096 +#define GL_TRANSPOSE_AFFINE_3D_NV 0x9098 +#define GL_UTF8_NV 0x909A +#define GL_UTF16_NV 0x909B +#define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C +#define GL_PATH_COMMAND_COUNT_NV 0x909D +#define GL_PATH_COORD_COUNT_NV 0x909E +#define GL_PATH_DASH_ARRAY_COUNT_NV 0x909F +#define GL_PATH_COMPUTED_LENGTH_NV 0x90A0 +#define GL_PATH_FILL_BOUNDING_BOX_NV 0x90A1 +#define GL_PATH_STROKE_BOUNDING_BOX_NV 0x90A2 +#define GL_SQUARE_NV 0x90A3 +#define GL_ROUND_NV 0x90A4 +#define GL_TRIANGULAR_NV 0x90A5 +#define GL_BEVEL_NV 0x90A6 +#define GL_MITER_REVERT_NV 0x90A7 +#define GL_MITER_TRUNCATE_NV 0x90A8 +#define GL_SKIP_MISSING_GLYPH_NV 0x90A9 +#define GL_USE_MISSING_GLYPH_NV 0x90AA +#define GL_PATH_ERROR_POSITION_NV 0x90AB +#define GL_PATH_FOG_GEN_MODE_NV 0x90AC +#define GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD +#define GL_ADJACENT_PAIRS_NV 0x90AE +#define GL_FIRST_TO_REST_NV 0x90AF +#define GL_PATH_GEN_MODE_NV 0x90B0 +#define GL_PATH_GEN_COEFF_NV 0x90B1 +#define GL_PATH_GEN_COLOR_FORMAT_NV 0x90B2 +#define GL_PATH_GEN_COMPONENTS_NV 0x90B3 +#define GL_PATH_STENCIL_FUNC_NV 0x90B7 +#define GL_PATH_STENCIL_REF_NV 0x90B8 +#define GL_PATH_STENCIL_VALUE_MASK_NV 0x90B9 +#define GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD +#define GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE +#define GL_PATH_COVER_DEPTH_FUNC_NV 0x90BF +#define GL_PATH_DASH_OFFSET_RESET_NV 0x90B4 +#define GL_MOVE_TO_RESETS_NV 0x90B5 +#define GL_MOVE_TO_CONTINUES_NV 0x90B6 +#define GL_CLOSE_PATH_NV 0x00 +#define GL_MOVE_TO_NV 0x02 +#define GL_RELATIVE_MOVE_TO_NV 0x03 +#define GL_LINE_TO_NV 0x04 +#define GL_RELATIVE_LINE_TO_NV 0x05 +#define GL_HORIZONTAL_LINE_TO_NV 0x06 +#define GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07 +#define GL_VERTICAL_LINE_TO_NV 0x08 +#define GL_RELATIVE_VERTICAL_LINE_TO_NV 0x09 +#define GL_QUADRATIC_CURVE_TO_NV 0x0A +#define GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B +#define GL_CUBIC_CURVE_TO_NV 0x0C +#define GL_RELATIVE_CUBIC_CURVE_TO_NV 0x0D +#define GL_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E +#define GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F +#define GL_SMOOTH_CUBIC_CURVE_TO_NV 0x10 +#define GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11 +#define GL_SMALL_CCW_ARC_TO_NV 0x12 +#define GL_RELATIVE_SMALL_CCW_ARC_TO_NV 0x13 +#define GL_SMALL_CW_ARC_TO_NV 0x14 +#define GL_RELATIVE_SMALL_CW_ARC_TO_NV 0x15 +#define GL_LARGE_CCW_ARC_TO_NV 0x16 +#define GL_RELATIVE_LARGE_CCW_ARC_TO_NV 0x17 +#define GL_LARGE_CW_ARC_TO_NV 0x18 +#define GL_RELATIVE_LARGE_CW_ARC_TO_NV 0x19 +#define GL_RESTART_PATH_NV 0xF0 +#define GL_DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2 +#define GL_DUP_LAST_CUBIC_CURVE_TO_NV 0xF4 +#define GL_RECT_NV 0xF6 +#define GL_CIRCULAR_CCW_ARC_TO_NV 0xF8 +#define GL_CIRCULAR_CW_ARC_TO_NV 0xFA +#define GL_CIRCULAR_TANGENT_ARC_TO_NV 0xFC +#define GL_ARC_TO_NV 0xFE +#define GL_RELATIVE_ARC_TO_NV 0xFF +#define GL_BOLD_BIT_NV 0x01 +#define GL_ITALIC_BIT_NV 0x02 +#define GL_GLYPH_WIDTH_BIT_NV 0x01 +#define GL_GLYPH_HEIGHT_BIT_NV 0x02 +#define GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04 +#define GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08 +#define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10 +#define GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20 +#define GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40 +#define GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80 +#define GL_GLYPH_HAS_KERNING_BIT_NV 0x100 +#define GL_FONT_X_MIN_BOUNDS_BIT_NV 0x00010000 +#define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000 +#define GL_FONT_X_MAX_BOUNDS_BIT_NV 0x00040000 +#define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000 +#define GL_FONT_UNITS_PER_EM_BIT_NV 0x00100000 +#define GL_FONT_ASCENDER_BIT_NV 0x00200000 +#define GL_FONT_DESCENDER_BIT_NV 0x00400000 +#define GL_FONT_HEIGHT_BIT_NV 0x00800000 +#define GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000 +#define GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000 +#define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000 +#define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000 +#define GL_FONT_HAS_KERNING_BIT_NV 0x10000000 +#define GL_PRIMARY_COLOR_NV 0x852C +#define GL_SECONDARY_COLOR_NV 0x852D +#define GL_ROUNDED_RECT_NV 0xE8 +#define GL_RELATIVE_ROUNDED_RECT_NV 0xE9 +#define GL_ROUNDED_RECT2_NV 0xEA +#define GL_RELATIVE_ROUNDED_RECT2_NV 0xEB +#define GL_ROUNDED_RECT4_NV 0xEC +#define GL_RELATIVE_ROUNDED_RECT4_NV 0xED +#define GL_ROUNDED_RECT8_NV 0xEE +#define GL_RELATIVE_ROUNDED_RECT8_NV 0xEF +#define GL_RELATIVE_RECT_NV 0xF7 +#define GL_FONT_GLYPHS_AVAILABLE_NV 0x9368 +#define GL_FONT_TARGET_UNAVAILABLE_NV 0x9369 +#define GL_FONT_UNAVAILABLE_NV 0x936A +#define GL_FONT_UNINTELLIGIBLE_NV 0x936B +#define GL_CONIC_CURVE_TO_NV 0x1A +#define GL_RELATIVE_CONIC_CURVE_TO_NV 0x1B +#define GL_FONT_NUM_GLYPH_INDICES_BIT_NV 0x20000000 +#define GL_STANDARD_FONT_FORMAT_NV 0x936C +#define GL_2_BYTES_NV 0x1407 +#define GL_3_BYTES_NV 0x1408 +#define GL_4_BYTES_NV 0x1409 +#define GL_EYE_LINEAR_NV 0x2400 +#define GL_OBJECT_LINEAR_NV 0x2401 +#define GL_CONSTANT_NV 0x8576 +#define GL_PATH_PROJECTION_NV 0x1701 +#define GL_PATH_MODELVIEW_NV 0x1700 +#define GL_PATH_MODELVIEW_STACK_DEPTH_NV 0x0BA3 +#define GL_PATH_MODELVIEW_MATRIX_NV 0x0BA6 +#define GL_PATH_MAX_MODELVIEW_STACK_DEPTH_NV 0x0D36 +#define GL_PATH_TRANSPOSE_MODELVIEW_MATRIX_NV 0x84E3 +#define GL_PATH_PROJECTION_STACK_DEPTH_NV 0x0BA4 +#define GL_PATH_PROJECTION_MATRIX_NV 0x0BA7 +#define GL_PATH_MAX_PROJECTION_STACK_DEPTH_NV 0x0D38 +#define GL_PATH_TRANSPOSE_PROJECTION_MATRIX_NV 0x84E4 +#define GL_FRAGMENT_INPUT_NV 0x936D + +typedef GLuint (QOPENGLF_APIENTRYP PFNGLGENPATHSNVPROC) (GLsizei range); +typedef void (QOPENGLF_APIENTRYP PFNGLDELETEPATHSNVPROC) (GLuint path, GLsizei range); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPATHNVPROC) (GLuint path); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOMMANDSNVPROC) (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOORDSNVPROC) (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSUBCOMMANDSNVPROC) (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSUBCOORDSNVPROC) (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTRINGNVPROC) (GLuint path, GLenum format, GLsizei length, const void *pathString); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHGLYPHSNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHGLYPHRANGENVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (QOPENGLF_APIENTRYP PFNGLWEIGHTPATHSNVPROC) (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); +typedef void (QOPENGLF_APIENTRYP PFNGLCOPYPATHNVPROC) (GLuint resultPath, GLuint srcPath); +typedef void (QOPENGLF_APIENTRYP PFNGLINTERPOLATEPATHSNVPROC) (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); +typedef void (QOPENGLF_APIENTRYP PFNGLTRANSFORMPATHNVPROC) (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, const GLint *value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERINVPROC) (GLuint path, GLenum pname, GLint value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, const GLfloat *value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERFNVPROC) (GLuint path, GLenum pname, GLfloat value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHDASHARRAYNVPROC) (GLuint path, GLsizei dashCount, const GLfloat *dashArray); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTENCILFUNCNVPROC) (GLenum func, GLint ref, GLuint mask); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTENCILDEPTHOFFSETNVPROC) (GLfloat factor, GLfloat units); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOVERDEPTHFUNCNVPROC) (GLenum func); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOLORGENNVPROC) (GLenum color, GLenum genMode, GLenum colorFormat, const GLfloat *coeffs); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHTEXGENNVPROC) (GLenum texCoordSet, GLenum genMode, GLint components, const GLfloat *coeffs); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHFOGGENNVPROC) (GLenum genMode); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERFILLPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERSTROKEPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, GLint *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, GLfloat *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOMMANDSNVPROC) (GLuint path, GLubyte *commands); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOORDSNVPROC) (GLuint path, GLfloat *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHDASHARRAYNVPROC) (GLuint path, GLfloat *dashArray); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHMETRICSNVPROC) (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHMETRICRANGENVPROC) (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHSPACINGNVPROC) (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOLORGENIVNVPROC) (GLenum color, GLenum pname, GLint *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOLORGENFVNVPROC) (GLenum color, GLenum pname, GLfloat *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHTEXGENIVNVPROC) (GLenum texCoordSet, GLenum pname, GLint *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHTEXGENFVNVPROC) (GLenum texCoordSet, GLenum pname, GLfloat *value); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPOINTINFILLPATHNVPROC) (GLuint path, GLuint mask, GLfloat x, GLfloat y); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPOINTINSTROKEPATHNVPROC) (GLuint path, GLfloat x, GLfloat y); +typedef GLfloat (QOPENGLF_APIENTRYP PFNGLGETPATHLENGTHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLPOINTALONGPATHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOAD3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOAD3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULT3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULT3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHGLYPHINDEXRANGENVPROC) (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint baseAndCount[2]); +typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (QOPENGLF_APIENTRYP PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC) (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPROGRAMRESOURCEFVNVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLfloat *params); +#endif + +#ifndef GL_FLAT +#define GL_FLAT 0x1D00 +#endif + +#ifndef GL_INVERT +#define GL_INVERT 0x150A +#endif + +#ifndef GL_EXT_direct_state_access +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC) (GLenum mode); +#endif + +// When building on a system with GLES 2.0 or 3.0, we may still compile the NVPR +// code path even though it's never used. Keep it compiling by defining the +// necessary ES 3.1 separable program constants. +#ifndef GL_FRAGMENT_SHADER_BIT +#define GL_FRAGMENT_SHADER_BIT 0x00000002 +#endif +#ifndef GL_UNIFORM +#define GL_UNIFORM 0x92E1 +#endif + +class QQuickNvprFunctionsPrivate; + +class QQuickNvprFunctions +{ +public: + QQuickNvprFunctions(); + ~QQuickNvprFunctions(); + + static QSurfaceFormat format(); + static bool isSupported(); + + bool create(); + + bool createFragmentOnlyPipeline(const char *fragmentShaderSource, GLuint *pipeline, GLuint *program); + + PFNGLGENPATHSNVPROC genPaths = nullptr; + PFNGLDELETEPATHSNVPROC deletePaths = nullptr; + PFNGLISPATHNVPROC isPath = nullptr; + PFNGLPATHCOMMANDSNVPROC pathCommands = nullptr; + PFNGLPATHCOORDSNVPROC pathCoords = nullptr; + PFNGLPATHSUBCOMMANDSNVPROC pathSubCommands = nullptr; + PFNGLPATHSUBCOORDSNVPROC pathSubCoords = nullptr; + PFNGLPATHSTRINGNVPROC pathString = nullptr; + PFNGLPATHGLYPHSNVPROC pathGlyphs = nullptr; + PFNGLPATHGLYPHRANGENVPROC pathGlyphRange = nullptr; + PFNGLWEIGHTPATHSNVPROC weightPaths = nullptr; + PFNGLCOPYPATHNVPROC copyPath = nullptr; + PFNGLINTERPOLATEPATHSNVPROC interpolatePaths = nullptr; + PFNGLTRANSFORMPATHNVPROC transformPath = nullptr; + PFNGLPATHPARAMETERIVNVPROC pathParameteriv = nullptr; + PFNGLPATHPARAMETERINVPROC pathParameteri = nullptr; + PFNGLPATHPARAMETERFVNVPROC pathParameterfv = nullptr; + PFNGLPATHPARAMETERFNVPROC pathParameterf = nullptr; + PFNGLPATHDASHARRAYNVPROC pathDashArray = nullptr; + PFNGLPATHSTENCILFUNCNVPROC pathStencilFunc = nullptr; + PFNGLPATHSTENCILDEPTHOFFSETNVPROC pathStencilDepthOffset = nullptr; + PFNGLSTENCILFILLPATHNVPROC stencilFillPath = nullptr; + PFNGLSTENCILSTROKEPATHNVPROC stencilStrokePath = nullptr; + PFNGLSTENCILFILLPATHINSTANCEDNVPROC stencilFillPathInstanced = nullptr; + PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC stencilStrokePathInstanced = nullptr; + PFNGLPATHCOVERDEPTHFUNCNVPROC pathCoverDepthFunc = nullptr; + PFNGLPATHCOLORGENNVPROC pathColorGen = nullptr; + PFNGLPATHTEXGENNVPROC pathTexGen = nullptr; + PFNGLPATHFOGGENNVPROC pathFogGen = nullptr; + PFNGLCOVERFILLPATHNVPROC coverFillPath = nullptr; + PFNGLCOVERSTROKEPATHNVPROC coverStrokePath = nullptr; + PFNGLCOVERFILLPATHINSTANCEDNVPROC coverFillPathInstanced = nullptr; + PFNGLCOVERSTROKEPATHINSTANCEDNVPROC coverStrokePathInstanced = nullptr; + PFNGLGETPATHPARAMETERIVNVPROC getPathParameteriv = nullptr; + PFNGLGETPATHPARAMETERFVNVPROC getPathParameterfv = nullptr; + PFNGLGETPATHCOMMANDSNVPROC getPathCommands = nullptr; + PFNGLGETPATHCOORDSNVPROC getPathCoords = nullptr; + PFNGLGETPATHDASHARRAYNVPROC getPathDashArray = nullptr; + PFNGLGETPATHMETRICSNVPROC getPathMetrics = nullptr; + PFNGLGETPATHMETRICRANGENVPROC getPathMetricRange = nullptr; + PFNGLGETPATHSPACINGNVPROC getPathSpacing = nullptr; + PFNGLGETPATHCOLORGENIVNVPROC getPathColorgeniv = nullptr; + PFNGLGETPATHCOLORGENFVNVPROC getPathColorgenfv = nullptr; + PFNGLGETPATHTEXGENIVNVPROC getPathTexGeniv = nullptr; + PFNGLGETPATHTEXGENFVNVPROC getPathTexGenfv = nullptr; + PFNGLISPOINTINFILLPATHNVPROC isPointInFillPath = nullptr; + PFNGLISPOINTINSTROKEPATHNVPROC isPointInStrokePath = nullptr; + PFNGLGETPATHLENGTHNVPROC getPathLength = nullptr; + PFNGLPOINTALONGPATHNVPROC getPointAlongPath = nullptr; + PFNGLMATRIXLOAD3X2FNVPROC matrixLoad3x2f = nullptr; + PFNGLMATRIXLOAD3X3FNVPROC matrixLoad3x3f = nullptr; + PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC matrixLoadTranspose3x3f = nullptr; + PFNGLMATRIXMULT3X2FNVPROC matrixMult3x2f = nullptr; + PFNGLMATRIXMULT3X3FNVPROC matrixMult3x3f = nullptr; + PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC matrixMultTranspose3x3f = nullptr; + PFNGLSTENCILTHENCOVERFILLPATHNVPROC stencilThenCoverFillPath = nullptr; + PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC stencilThenCoverStrokePath = nullptr; + PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC stencilThenCoverFillPathInstanced = nullptr; + PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC stencilThenCoverStrokePathInstanced = nullptr; + PFNGLPATHGLYPHINDEXRANGENVPROC pathGlyphIndexRange = nullptr; + PFNGLPATHGLYPHINDEXARRAYNVPROC pathGlyphIndexArray = nullptr; + PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC pathMemoryGlyphIndexArray = nullptr; + PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC programPathFragmentInputGen = nullptr; + PFNGLGETPROGRAMRESOURCEFVNVPROC getProgramResourcefv = nullptr; + + PFNGLMATRIXLOADFEXTPROC matrixLoadf = nullptr; + PFNGLMATRIXLOADIDENTITYEXTPROC matrixLoadIdentity = nullptr; + +private: + QQuickNvprFunctionsPrivate *d; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_OPENGL + +#endif // QQUICKNVPRFUNCTIONS_P_H diff --git a/src/quick/util/qquicknvprfunctions_p_p.h b/src/quick/util/qquicknvprfunctions_p_p.h new file mode 100644 index 0000000000..6df20566af --- /dev/null +++ b/src/quick/util/qquicknvprfunctions_p_p.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKNVPRFUNCTIONS_P_P_H +#define QQUICKNVPRFUNCTIONS_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qquicknvprfunctions_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickNvprFunctionsPrivate +{ +public: + QQuickNvprFunctionsPrivate(QQuickNvprFunctions *q_ptr) : q(q_ptr) { } + + bool resolve(); + + QQuickNvprFunctions *q; +}; + +QT_END_NAMESPACE + +#endif // QQUICKNVPRFUNCTIONS_P_P_H diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp index 25a4433a9b..a6fa21d696 100644 --- a/src/quick/util/qquickpath.cpp +++ b/src/quick/util/qquickpath.cpp @@ -68,7 +68,7 @@ QT_BEGIN_NAMESPACE \instantiates QQuickPath \inqmlmodule QtQuick \ingroup qtquick-animation-paths - \brief Defines a path for use by \l PathView + \brief Defines a path for use by \l PathView and \l PathItem A Path is composed of one or more path segments - PathLine, PathQuad, PathCubic, PathArc, PathCurve, PathSvg. @@ -79,7 +79,7 @@ QT_BEGIN_NAMESPACE PathAttribute allows named attributes with values to be defined along the path. - \sa PathView, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic, PathArc, PathCurve, PathSvg + \sa PathView, PathItem, PathAttribute, PathPercent, PathLine, PathMove, PathQuad, PathCubic, PathArc, PathCurve, PathSvg */ QQuickPath::QQuickPath(QObject *parent) : QObject(*(new QQuickPathPrivate), parent) @@ -1017,7 +1017,7 @@ void QQuickPathAttribute::setValue(qreal value) } \endqml - \sa Path, PathQuad, PathCubic, PathArc, PathCurve, PathSvg + \sa Path, PathQuad, PathCubic, PathArc, PathCurve, PathSvg, PathMove */ /*! @@ -1059,6 +1059,66 @@ void QQuickPathLine::addToPath(QPainterPath &path, const QQuickPathData &data) /****************************************************************************/ +/*! + \qmltype PathMove + \instantiates QQuickPathMove + \inqmlmodule QtQuick + \ingroup qtquick-animation-paths + \brief Moves the Path's position + + While not relevant with PathView, for Path elements used with PathItem it + is important to distinguish between the operations of drawing a straight + line and moving the path position without drawing anything. + + \note PathMove should not be used in a Path associated with a PathView. Use + PathLine instead. + + The example below creates a path consisting of two horizontal lines with + some empty space between them. All three segments have a width of 100: + + \qml + Path { + startX: 0; startY: 100 + PathLine { relativeX: 100; y: 100 } + PathMove { relativeX: 100; y: 100 } + PathLine { relativeX: 100; y: 100 } + } + \endqml + + \sa Path, PathQuad, PathCubic, PathArc, PathCurve, PathSvg, PathLine +*/ + +/*! + \qmlproperty real QtQuick::PathMove::x + \qmlproperty real QtQuick::PathMove::y + + Defines the position to move to. + + \sa relativeX, relativeY +*/ + +/*! + \qmlproperty real QtQuick::PathMove::relativeX + \qmlproperty real QtQuick::PathMove::relativeY + + Defines the position to move to relative to its start. + + If both a relative and absolute end position are specified for a single axis, the relative + position will be used. + + Relative and absolute positions can be mixed, for example it is valid to set a relative x + and an absolute y. + + \sa x, y +*/ + +void QQuickPathMove::addToPath(QPainterPath &path, const QQuickPathData &data) +{ + path.moveTo(positionForCurve(data, path.currentPosition())); +} + +/****************************************************************************/ + /*! \qmltype PathQuad \instantiates QQuickPathQuad @@ -1655,6 +1715,7 @@ void QQuickPathArc::setRadiusX(qreal radius) _radiusX = radius; emit radiusXChanged(); + emit changed(); } qreal QQuickPathArc::radiusY() const @@ -1669,6 +1730,7 @@ void QQuickPathArc::setRadiusY(qreal radius) _radiusY = radius; emit radiusYChanged(); + emit changed(); } /*! @@ -1702,6 +1764,7 @@ void QQuickPathArc::setUseLargeArc(bool largeArc) _useLargeArc = largeArc; emit useLargeArcChanged(); + emit changed(); } /*! @@ -1733,6 +1796,7 @@ void QQuickPathArc::setDirection(ArcDirection direction) _direction = direction; emit directionChanged(); + emit changed(); } void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data) diff --git a/src/quick/util/qquickpath_p.h b/src/quick/util/qquickpath_p.h index 06ad389b49..283283f377 100644 --- a/src/quick/util/qquickpath_p.h +++ b/src/quick/util/qquickpath_p.h @@ -159,6 +159,15 @@ public: void addToPath(QPainterPath &path, const QQuickPathData &) override; }; +class Q_QUICK_PRIVATE_EXPORT QQuickPathMove : public QQuickCurve +{ + Q_OBJECT +public: + QQuickPathMove(QObject *parent=0) : QQuickCurve(parent) {} + + void addToPath(QPainterPath &path, const QQuickPathData &) override; +}; + class Q_QUICK_PRIVATE_EXPORT QQuickPathQuad : public QQuickCurve { Q_OBJECT @@ -459,6 +468,7 @@ QML_DECLARE_TYPE(QQuickPathElement) QML_DECLARE_TYPE(QQuickPathAttribute) QML_DECLARE_TYPE(QQuickCurve) QML_DECLARE_TYPE(QQuickPathLine) +QML_DECLARE_TYPE(QQuickPathMove) QML_DECLARE_TYPE(QQuickPathQuad) QML_DECLARE_TYPE(QQuickPathCubic) QML_DECLARE_TYPE(QQuickPathCatmullRomCurve) diff --git a/src/quick/util/util.pri b/src/quick/util/util.pri index 1ef1018a31..22f23f7598 100644 --- a/src/quick/util/util.pri +++ b/src/quick/util/util.pri @@ -74,4 +74,11 @@ qtConfig(quick-path) { $$PWD/qquickpath_p.h \ $$PWD/qquickpath_p_p.h \ $$PWD/qquickpathinterpolator_p.h + qtConfig(opengl) { + SOURCES += \ + $$PWD/qquicknvprfunctions.cpp + HEADERS += \ + $$PWD/qquicknvprfunctions_p.h \ + $$PWD/qquicknvprfunctions_p_p.h + } } -- cgit v1.2.3 From bf7c6226264bd45095711b2c0556c42b6f267f72 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 19 Dec 2016 13:53:29 +0100 Subject: software backend: Fix clipping of QSGRenderNodes Change-Id: I27aa5f94165fb07807d2bb711d81eade552b9f76 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemsoftwarerenderer.cpp | 8 ++++---- .../adaptations/software/qsgsoftwarerenderablenode.cpp | 17 ++++++++++------- src/quick/scenegraph/coreapi/qsgrendernode.cpp | 5 ++++- 3 files changed, 18 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitemsoftwarerenderer.cpp b/src/quick/items/qquickpathitemsoftwarerenderer.cpp index 40732e4bf9..f089904fdf 100644 --- a/src/quick/items/qquickpathitemsoftwarerenderer.cpp +++ b/src/quick/items/qquickpathitemsoftwarerenderer.cpp @@ -202,12 +202,12 @@ void QQuickPathItemSoftwareRenderNode::render(const RenderState *state) QPainter *p = static_cast(rif->getResource(m_item->window(), QSGRendererInterface::PainterResource)); Q_ASSERT(p); - p->setTransform(matrix()->toTransform()); - p->setOpacity(inheritedOpacity()); - const QRegion *clipRegion = state->clipRegion(); if (clipRegion && !clipRegion->isEmpty()) - p->setClipRegion(*clipRegion, Qt::IntersectClip); + p->setClipRegion(*clipRegion, Qt::ReplaceClip); // must be done before setTransform + + p->setTransform(matrix()->toTransform()); + p->setOpacity(inheritedOpacity()); p->setPen(!qFuzzyIsNull(m_pen.widthF()) && m_pen.color() != Qt::transparent ? m_pen : Qt::NoPen); p->setBrush(m_brush.color() != Qt::transparent ? m_brush : Qt::NoBrush); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp index 59c47db0c4..7adb39d9a8 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp @@ -254,18 +254,21 @@ QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaqu QMatrix4x4 m = m_transform; rd->m_matrix = &m; rd->m_opacity = m_opacity; - RenderNodeState rs; - rs.cr = m_clipRegion; - const QRect br = m_handle.renderNode->flags().testFlag(QSGRenderNode::BoundedRectRendering) - ? m_boundingRect : - QRect(0, 0, painter->device()->width(), painter->device()->height()); + // all the clip region below is in world coordinates, taking m_transform into account already + QRegion cr = m_dirtyRegion; + if (m_clipRegion.rectCount() > 1) + cr &= m_clipRegion; painter->save(); - painter->setClipRegion(br, Qt::ReplaceClip); + RenderNodeState rs; + rs.cr = cr; m_handle.renderNode->render(&rs); painter->restore(); + const QRect br = m_handle.renderNode->flags().testFlag(QSGRenderNode::BoundedRectRendering) + ? m_boundingRect // already mapped to world + : QRect(0, 0, painter->device()->width(), painter->device()->height()); m_previousDirtyRegion = QRegion(br); m_isDirty = false; m_dirtyRegion = QRegion(); @@ -276,7 +279,7 @@ QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaqu painter->save(); painter->setOpacity(m_opacity); - // Set clipRegion to m_dirtyRegion (in world coordinates) + // Set clipRegion to m_dirtyRegion (in world coordinates, so must be done before the setTransform below) // as m_dirtyRegion already accounts for clipRegion painter->setClipRegion(m_dirtyRegion, Qt::ReplaceClip); if (m_clipRegion.rectCount() > 1) diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.cpp b/src/quick/scenegraph/coreapi/qsgrendernode.cpp index 1bc0210b72..3007e0000c 100644 --- a/src/quick/scenegraph/coreapi/qsgrendernode.cpp +++ b/src/quick/scenegraph/coreapi/qsgrendernode.cpp @@ -359,7 +359,10 @@ QSGRenderNode::RenderState::~RenderState() of the render state is not in use. However, the clip region that can be set on the QPainter still has to be communicated since reconstructing this manually in render() is not reasonable. It can therefore be queried via - this function. + this function. The region is in world coordinates and can be passed + to QPainter::setClipRegion() with Qt::ReplaceClip. This must be done before + calling QPainter::setTransform() since the clip region is already mapped to + the transform provided in QSGRenderNode::matrix(). */ /*! -- cgit v1.2.3 From 79831caa0533ad5f48322568557622596b85ed0f Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 21 Dec 2016 13:15:38 +0100 Subject: Treat 0 as a valid strokeWidth value Use negative values as the trigger to disable stroking altogether. This matches both QPainter and NVPR better. Change-Id: I51395ae310fce8a8da0c06174eafa1dc17aae1db Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitem.cpp | 2 +- src/quick/items/qquickpathitemgenericrenderer.cpp | 6 ++++-- src/quick/items/qquickpathitemgenericrenderer_p.h | 1 + src/quick/items/qquickpathitemnvprrenderer.cpp | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp index 0d2c7a8bbe..720916c900 100644 --- a/src/quick/items/qquickpathitem.cpp +++ b/src/quick/items/qquickpathitem.cpp @@ -455,7 +455,7 @@ QSGNode *QQuickPathItemPrivate::createRenderNode() return node; const bool hasFill = fillColor != Qt::transparent; - const bool hasStroke = !qFuzzyIsNull(strokeWidth) && strokeColor != Qt::transparent; + const bool hasStroke = strokeWidth >= 0.0f && strokeColor != Qt::transparent; switch (ri->graphicsApi()) { #ifndef QT_NO_OPENGL diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp index 9bd03c0e51..4b15daef9a 100644 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -145,7 +145,9 @@ void QQuickPathItemGenericRenderer::setStrokeColor(const QColor &color) void QQuickPathItemGenericRenderer::setStrokeWidth(qreal w) { - m_pen.setWidthF(w); + m_strokeWidth = w; + if (w >= 0.0f) + m_pen.setWidthF(w); m_syncDirty |= DirtyGeom; } @@ -311,7 +313,7 @@ void QQuickPathItemGenericRenderer::updatePathRenderNode() m_effectiveDirty |= DirtyGeom; } - if (qFuzzyIsNull(m_pen.widthF()) || m_strokeColor.a == 0) { + 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) { diff --git a/src/quick/items/qquickpathitemgenericrenderer_p.h b/src/quick/items/qquickpathitemgenericrenderer_p.h index c186959c88..a094b9fca6 100644 --- a/src/quick/items/qquickpathitemgenericrenderer_p.h +++ b/src/quick/items/qquickpathitemgenericrenderer_p.h @@ -107,6 +107,7 @@ private: QTriangulatingStroker m_stroker; QDashedStrokeProcessor m_dashStroker; + float m_strokeWidth; QPen m_pen; Color4ub m_strokeColor; Color4ub m_fillColor; diff --git a/src/quick/items/qquickpathitemnvprrenderer.cpp b/src/quick/items/qquickpathitemnvprrenderer.cpp index d07a63c86d..e0c458bfca 100644 --- a/src/quick/items/qquickpathitemnvprrenderer.cpp +++ b/src/quick/items/qquickpathitemnvprrenderer.cpp @@ -529,7 +529,7 @@ void QQuickPathItemNvprRenderNode::render(const RenderState *state) nvpr.stencilThenCoverFillPath(m_path, m_fillRule, 0xFF, GL_BOUNDING_BOX_NV); } - if (!qFuzzyIsNull(m_strokeWidth) && !qFuzzyIsNull(m_strokeColor.w())) { + if (m_strokeWidth >= 0.0f && !qFuzzyIsNull(m_strokeColor.w())) { if (m_fillGradientActive) mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], -- cgit v1.2.3 From aa24b8938bb03e688633e544dddeca5aff91940e Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 19 Dec 2016 13:54:58 +0100 Subject: Stencil clipping for NVPR Fix also the fill rule interpretation on NVPR - it was the opposite of what QPainter was doing. Change-Id: I23ff3b20e3b066d4b4e07aaa68b7da1e09d9127d Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitem.cpp | 2 +- src/quick/items/qquickpathitemnvprrenderer.cpp | 274 +++++++++++++++++++++---- src/quick/items/qquickpathitemnvprrenderer_p.h | 33 +++ 3 files changed, 270 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp index 720916c900..0a4c721a74 100644 --- a/src/quick/items/qquickpathitem.cpp +++ b/src/quick/items/qquickpathitem.cpp @@ -426,7 +426,7 @@ void QQuickPathItemPrivate::createRenderer() case QSGRendererInterface::OpenGL: if (QQuickPathItemNvprRenderNode::isSupported()) { rendererType = QQuickPathItem::NvprRenderer; - renderer = new QQuickPathItemNvprRenderer; + renderer = new QQuickPathItemNvprRenderer(q); } else { rendererType = QQuickPathItem::GeometryRenderer; renderer = new QQuickPathItemGenericRenderer(q); diff --git a/src/quick/items/qquickpathitemnvprrenderer.cpp b/src/quick/items/qquickpathitemnvprrenderer.cpp index e0c458bfca..bd88023891 100644 --- a/src/quick/items/qquickpathitemnvprrenderer.cpp +++ b/src/quick/items/qquickpathitemnvprrenderer.cpp @@ -39,6 +39,9 @@ #include "qquickpathitemnvprrenderer_p.h" #include +#include +#include +#include #include QT_BEGIN_NAMESPACE @@ -307,10 +310,10 @@ void QQuickPathItemNvprRenderer::updatePathRenderNode() if (m_dirty & DirtyFillRule) { switch (m_fillRule) { case QQuickPathItem::OddEvenFill: - m_node->m_fillRule = GL_COUNT_UP_NV; + m_node->m_fillRule = GL_INVERT; break; case QQuickPathItem::WindingFill: - m_node->m_fillRule = GL_INVERT; + m_node->m_fillRule = GL_COUNT_UP_NV; break; default: Q_UNREACHABLE(); @@ -360,6 +363,13 @@ void QQuickPathItemNvprRenderNode::releaseResources() nvpr.deletePaths(m_path, 1); m_path = 0; } + + if (m_fallbackFbo) { + delete m_fallbackFbo; + m_fallbackFbo = nullptr; + } + + m_fallbackBlitter.destroy(); } void QQuickNvprMaterialManager::create(QQuickNvprFunctions *nvpr) @@ -465,8 +475,95 @@ void QQuickPathItemNvprRenderNode::updatePath() } } +void QQuickPathItemNvprRenderNode::renderStroke(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()); + f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); + + nvpr.stencilThenCoverStrokePath(m_path, strokeStencilValue, writeMask, GL_CONVEX_HULL_NV); +} + +void QQuickPathItemNvprRenderNode::renderFill() +{ + QQuickNvprMaterialManager::MaterialDesc *mtl = nullptr; + if (m_fillGradientActive) { + mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatLinearGradient); + QSGTexture *tx = QQuickPathItemGradientCache::currentCache()->get(m_fillGradient); + tx->bind(); + // uv = vec2(coeff[0] * x + coeff[1] * y + coeff[2], coeff[3] * x + coeff[4] * y + coeff[5]) + // where x and y are in path coordinate space, which is just what + // we need since the gradient's start and stop are in that space too. + GLfloat coeff[6] = { 1, 0, 0, + 0, 1, 0 }; + nvpr.programPathFragmentInputGen(mtl->prg, 0, GL_OBJECT_LINEAR_NV, 2, coeff); + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[2], m_fillGradient.start.x(), m_fillGradient.start.y()); + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[3], m_fillGradient.end.x(), m_fillGradient.end.y()); + } else { + mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); + f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], + m_fillColor.x(), m_fillColor.y(), m_fillColor.z(), m_fillColor.w()); + } + f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); + + const int writeMask = 0xFF; + nvpr.stencilThenCoverFillPath(m_path, m_fillRule, writeMask, GL_BOUNDING_BOX_NV); +} + +void QQuickPathItemNvprRenderNode::renderOffscreenFill() +{ + QQuickWindow *w = m_item->window(); + const qreal dpr = w->effectiveDevicePixelRatio(); + QSize itemSize = QSize(m_item->width(), m_item->height()) * dpr; + QSize rtSize = w->renderTargetSize(); + if (rtSize.isEmpty()) + rtSize = w->size() * dpr; + + if (m_fallbackFbo && m_fallbackFbo->size() != itemSize) { + delete m_fallbackFbo; + m_fallbackFbo = nullptr; + } + if (!m_fallbackFbo) + m_fallbackFbo = new QOpenGLFramebufferObject(itemSize, QOpenGLFramebufferObject::CombinedDepthStencil); + if (!m_fallbackFbo->bind()) + return; + + f->glViewport(0, 0, itemSize.width(), itemSize.height()); + f->glClearColor(0, 0, 0, 0); + f->glClearStencil(0); + f->glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); + f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + + nvpr.matrixLoadIdentity(GL_PATH_MODELVIEW_NV); + QMatrix4x4 proj; + proj.ortho(0, itemSize.width(), itemSize.height(), 0, 1, -1); + nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, proj.constData()); + + renderFill(); + + m_fallbackFbo->release(); + f->glViewport(0, 0, rtSize.width(), rtSize.height()); +} + +void QQuickPathItemNvprRenderNode::setupStencilForCover(bool stencilClip, int sv) +{ + if (!stencilClip) { + // Assume stencil buffer is cleared to 0 for each frame. + // Within the frame dppass=GL_ZERO for glStencilOp ensures stencil is reset and so no need to clear. + f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); + f->glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO); + } else { + f->glStencilFunc(GL_LESS, sv, 0xFF); // pass if (sv & 0xFF) < (stencil_value & 0xFF) + f->glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); // dppass: replace with the original value (clip's stencil ref value) + } +} + void QQuickPathItemNvprRenderNode::render(const RenderState *state) { + f = QOpenGLContext::currentContext()->extraFunctions(); + if (!nvprInited) { if (!nvpr.create()) { qWarning("NVPR init failed"); @@ -478,22 +575,23 @@ void QQuickPathItemNvprRenderNode::render(const RenderState *state) updatePath(); - QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); f->glUseProgram(0); - QQuickNvprMaterialManager::MaterialDesc *mtl; - if (m_fillGradientActive) - mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatLinearGradient); - else - mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); - if (!mtl) - return; - - // Assume stencil buffer is cleared to 0 for each frame. - // Within the frame dppass=GL_ZERO for glStencilOp ensures stencil is reset and so no need to clear. f->glStencilMask(~0); f->glEnable(GL_STENCIL_TEST); - f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); - f->glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO); + + const bool stencilClip = state->stencilEnabled(); + // 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()); + + 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(); + } // Depth test against the opaque batches rendered before. f->glEnable(GL_DEPTH_TEST); @@ -509,35 +607,43 @@ void QQuickPathItemNvprRenderNode::render(const RenderState *state) f->glEnable(GL_SCISSOR_TEST); } - if (!qFuzzyIsNull(m_fillColor.w()) || m_fillGradientActive) { - if (m_fillGradientActive) { - QSGTexture *tx = QQuickPathItemGradientCache::currentCache()->get(m_fillGradient); - tx->bind(); - // uv = vec2(coeff[0] * x + coeff[1] * y + coeff[2], coeff[3] * x + coeff[4] * y + coeff[5]) - // where x and y are in path coordinate space, which is just what - // we need since the gradient's start and stop are in that space too. - GLfloat coeff[6] = { 1, 0, 0, - 0, 1, 0 }; - nvpr.programPathFragmentInputGen(mtl->prg, 0, GL_OBJECT_LINEAR_NV, 2, coeff); - f->glProgramUniform2f(mtl->prg, mtl->uniLoc[2], m_fillGradient.start.x(), m_fillGradient.start.y()); - f->glProgramUniform2f(mtl->prg, mtl->uniLoc[3], m_fillGradient.end.x(), m_fillGradient.end.y()); + // Fill! + if (hasFill) { + if (!stencilClip) { + setupStencilForCover(false, 0); + renderFill(); } else { - f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], - m_fillColor.x(), m_fillColor.y(), m_fillColor.z(), m_fillColor.w()); + 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()); } - f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); - nvpr.stencilThenCoverFillPath(m_path, m_fillRule, 0xFF, GL_BOUNDING_BOX_NV); } - if (m_strokeWidth >= 0.0f && !qFuzzyIsNull(m_strokeColor.w())) { - if (m_fillGradientActive) - mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); - f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], - m_strokeColor.x(), m_strokeColor.y(), m_strokeColor.z(), m_strokeColor.w()); - f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); - nvpr.stencilThenCoverStrokePath(m_path, 0x1, ~0, GL_CONVEX_HULL_NV); + // 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(strokeStencilValue, writeMask); } + if (stencilClip) + nvpr.pathStencilFunc(GL_ALWAYS, 0, ~0); + f->glBindProgramPipeline(0); m_dirty = 0; @@ -564,4 +670,96 @@ bool QQuickPathItemNvprRenderNode::isSupported() return !nvprDisabled && QQuickNvprFunctions::isSupported(); } +bool QQuickNvprBlitter::create() +{ + if (isCreated()) + destroy(); + + m_program = new QOpenGLShaderProgram; + if (QOpenGLContext::currentContext()->format().profile() == QSurfaceFormat::CoreProfile) { + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/shadereffect_core.vert")); + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/shadereffect_core.frag")); + } else { + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/shadereffect.vert")); + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/shadereffect.frag")); + } + m_program->bindAttributeLocation("qt_Vertex", 0); + m_program->bindAttributeLocation("qt_MultiTexCoord0", 1); + if (!m_program->link()) + return false; + + m_matrixLoc = m_program->uniformLocation("qt_Matrix"); + m_opacityLoc = m_program->uniformLocation("qt_Opacity"); + + m_buffer = new QOpenGLBuffer; + if (!m_buffer->create()) + return false; + m_buffer->bind(); + m_buffer->allocate(4 * sizeof(GLfloat) * 6); + m_buffer->release(); + + return true; +} + +void QQuickNvprBlitter::destroy() +{ + if (m_program) { + delete m_program; + m_program = nullptr; + } + if (m_buffer) { + delete m_buffer; + m_buffer = nullptr; + } +} + +void QQuickNvprBlitter::texturedQuad(GLuint textureId, const QSize &size, + const QMatrix4x4 &proj, const QMatrix4x4 &modelview, + float opacity) +{ + QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); + + m_program->bind(); + + QMatrix4x4 m = proj * modelview; + m_program->setUniformValue(m_matrixLoc, m); + m_program->setUniformValue(m_opacityLoc, opacity); + + m_buffer->bind(); + + if (size != m_prevSize) { + m_prevSize = size; + + QPointF p0(size.width() - 1, size.height() - 1); + QPointF p1(0, 0); + QPointF p2(0, size.height() - 1); + QPointF p3(size.width() - 1, 0); + + GLfloat vertices[6 * 4] = { + GLfloat(p0.x()), GLfloat(p0.y()), 1, 0, + GLfloat(p1.x()), GLfloat(p1.y()), 0, 1, + GLfloat(p2.x()), GLfloat(p2.y()), 0, 0, + + GLfloat(p0.x()), GLfloat(p0.y()), 1, 0, + GLfloat(p3.x()), GLfloat(p3.y()), 1, 1, + GLfloat(p1.x()), GLfloat(p1.y()), 0, 1, + }; + + m_buffer->write(0, vertices, sizeof(vertices)); + } + + m_program->enableAttributeArray(0); + m_program->enableAttributeArray(1); + f->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0); + f->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (const void *) (2 * sizeof(GLfloat))); + + f->glBindTexture(GL_TEXTURE_2D, textureId); + + f->glDrawArrays(GL_TRIANGLES, 0, 6); + + f->glBindTexture(GL_TEXTURE_2D, 0); + m_buffer->release(); + m_program->release(); +} + QT_END_NAMESPACE diff --git a/src/quick/items/qquickpathitemnvprrenderer_p.h b/src/quick/items/qquickpathitemnvprrenderer_p.h index 0075572324..7dd2d564c5 100644 --- a/src/quick/items/qquickpathitemnvprrenderer_p.h +++ b/src/quick/items/qquickpathitemnvprrenderer_p.h @@ -63,6 +63,9 @@ QT_BEGIN_NAMESPACE class QQuickPathItemNvprRenderNode; +class QOpenGLFramebufferObject; +class QOpenGLBuffer; +class QOpenGLExtraFunctions; class QQuickPathItemNvprRenderer : public QQuickAbstractPathRenderer { @@ -77,6 +80,10 @@ public: DirtyAll = 0xFF }; + QQuickPathItemNvprRenderer(QQuickItem *item) + : m_item(item) + { } + void beginSync() override; void setPath(const QQuickPath *path) override; void setStrokeColor(const QColor &color) override; @@ -101,6 +108,7 @@ public: private: void convertPath(const QQuickPath *path); + QQuickItem *m_item; QQuickPathItemNvprRenderNode *m_node = nullptr; int m_dirty = 0; @@ -146,6 +154,24 @@ private: MaterialDesc m_materials[NMaterials]; }; +class QQuickNvprBlitter +{ +public: + bool create(); + void destroy(); + bool isCreated() const { return m_program != nullptr; } + void texturedQuad(GLuint textureId, const QSize &size, + const QMatrix4x4 &proj, const QMatrix4x4 &modelview, + float opacity); + +private: + QOpenGLShaderProgram *m_program = nullptr; + QOpenGLBuffer *m_buffer = nullptr; + int m_matrixLoc; + int m_opacityLoc; + QSize m_prevSize; +}; + class QQuickPathItemNvprRenderNode : public QSGRenderNode { public: @@ -162,6 +188,10 @@ public: private: void updatePath(); + void renderStroke(int strokeStencilValue, int writeMask); + void renderFill(); + void renderOffscreenFill(); + void setupStencilForCover(bool stencilClip, int sv); static bool nvprInited; static QQuickNvprFunctions nvpr; @@ -183,6 +213,9 @@ private: QVector m_dashPattern; bool m_fillGradientActive; QQuickPathItemGradientCache::GradientDesc m_fillGradient; + QOpenGLFramebufferObject *m_fallbackFbo = nullptr; + QQuickNvprBlitter m_fallbackBlitter; + QOpenGLExtraFunctions *f = nullptr; friend class QQuickPathItemNvprRenderer; }; -- cgit v1.2.3 From 917a9382df8a5a673556a21b1b46e4343231d674 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 4 Jan 2017 14:14:04 +0100 Subject: Fix negative stroke width for software PathItem Like the generic one, this also needs to deal with the fact that QPen does not take negative widths. Store the value separately. Change-Id: Ia332c2d83e98c03125a05c838cb8184346f8303a Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemsoftwarerenderer.cpp | 10 +++++++--- src/quick/items/qquickpathitemsoftwarerenderer_p.h | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitemsoftwarerenderer.cpp b/src/quick/items/qquickpathitemsoftwarerenderer.cpp index f089904fdf..c7b52e641f 100644 --- a/src/quick/items/qquickpathitemsoftwarerenderer.cpp +++ b/src/quick/items/qquickpathitemsoftwarerenderer.cpp @@ -61,7 +61,9 @@ void QQuickPathItemSoftwareRenderer::setStrokeColor(const QColor &color) void QQuickPathItemSoftwareRenderer::setStrokeWidth(qreal w) { - m_pen.setWidthF(w); + m_strokeWidth = w; + if (w >= 0.0f) + m_pen.setWidthF(w); m_dirty |= DirtyPen; } @@ -169,8 +171,10 @@ void QQuickPathItemSoftwareRenderer::updatePathRenderNode() if (m_dirty & DirtyFillRule) m_node->m_path.setFillRule(m_fillRule); - if (m_dirty & DirtyPen) + if (m_dirty & DirtyPen) { m_node->m_pen = m_pen; + m_node->m_strokeWidth = m_strokeWidth; + } if (m_dirty & DirtyBrush) m_node->m_brush = m_brush; @@ -209,7 +213,7 @@ void QQuickPathItemSoftwareRenderNode::render(const RenderState *state) p->setTransform(matrix()->toTransform()); p->setOpacity(inheritedOpacity()); - p->setPen(!qFuzzyIsNull(m_pen.widthF()) && m_pen.color() != Qt::transparent ? m_pen : Qt::NoPen); + 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); diff --git a/src/quick/items/qquickpathitemsoftwarerenderer_p.h b/src/quick/items/qquickpathitemsoftwarerenderer_p.h index 584771425d..6c7d052596 100644 --- a/src/quick/items/qquickpathitemsoftwarerenderer_p.h +++ b/src/quick/items/qquickpathitemsoftwarerenderer_p.h @@ -94,6 +94,7 @@ private: QPainterPath m_path; QPen m_pen; + float m_strokeWidth; QColor m_fillColor; QBrush m_brush; Qt::FillRule m_fillRule; @@ -117,6 +118,7 @@ private: QPainterPath m_path; QPen m_pen; + float m_strokeWidth; QBrush m_brush; friend class QQuickPathItemSoftwareRenderer; -- cgit v1.2.3 From 81d204e2a5118b2d81862fa9d9a45e5522396a45 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 4 Jan 2017 14:31:23 +0100 Subject: Add X axis rotation property to PathArc It is a standard feature of elliptical arc. While perhaps deemed too advanced for PathView purposes, rendering the path using PathItem must offer the ability to specify a non-zero X axis rotation for the ellipses of which the arc is a section of. Change-Id: I53f01713b7e0e97c40f22d75d46f75a140830683 Reviewed-by: Andy Nichols --- src/quick/doc/images/declarative-arcrotation.png | Bin 0 -> 4315 bytes src/quick/doc/snippets/qml/path/arcrotation.qml | 52 +++++++++++++++++++++++ src/quick/items/qquickitemsmodule.cpp | 1 + src/quick/util/qquickpath.cpp | 38 ++++++++++++++++- src/quick/util/qquickpath_p.h | 8 +++- 5 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 src/quick/doc/images/declarative-arcrotation.png create mode 100644 src/quick/doc/snippets/qml/path/arcrotation.qml (limited to 'src') diff --git a/src/quick/doc/images/declarative-arcrotation.png b/src/quick/doc/images/declarative-arcrotation.png new file mode 100644 index 0000000000..03f009bc12 Binary files /dev/null and b/src/quick/doc/images/declarative-arcrotation.png differ diff --git a/src/quick/doc/snippets/qml/path/arcrotation.qml b/src/quick/doc/snippets/qml/path/arcrotation.qml new file mode 100644 index 0000000000..c73d67ff17 --- /dev/null +++ b/src/quick/doc/snippets/qml/path/arcrotation.qml @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.9 +//![0] +Path { + startX: 50; startY: 100 + + PathArc { + x: 150; y: 100 + radiusX: 50; radiusY: 20 + xAxisRotation: 45 + } +} +//![0] diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index b0d7f7f8a3..9c70daab1f 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -373,6 +373,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType(uri, 2, 9, "MouseArea"); #if QT_CONFIG(quick_path) + qmlRegisterType(uri, 2, 9, "PathArc"); qmlRegisterType(uri, 2, 9, "PathMove"); qmlRegisterType(uri, 2, 9, "PathItem"); qmlRegisterType(uri, 2, 9, "PathGradientStop"); diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp index a6fa21d696..228056fdb5 100644 --- a/src/quick/util/qquickpath.cpp +++ b/src/quick/util/qquickpath.cpp @@ -1799,6 +1799,42 @@ void QQuickPathArc::setDirection(ArcDirection direction) emit changed(); } +/*! + \qmlproperty real QtQuick::PathArc::xAxisRotation + + Defines the rotation of the arc, in degrees. The default value is 0. + + An arc is a section of circles or ellipses. Given the radius and the start + and end points, there are two ellipses that connect the points. This + property defines the rotation of the X axis of these ellipses. + + \note The value is only useful when the x and y radius differ, meaning the + arc is a section of ellipses. + + The following QML demonstrates how different radius values can be used to change + the shape of the arc: + \table + \row + \li \image declarative-arcrotation.png + \li \snippet qml/path/arcrotation.qml 0 + \endtable +*/ + +qreal QQuickPathArc::xAxisRotation() const +{ + return _xAxisRotation; +} + +void QQuickPathArc::setXAxisRotation(qreal rotation) +{ + if (_xAxisRotation == rotation) + return; + + _xAxisRotation = rotation; + emit xAxisRotationChanged(); + emit changed(); +} + void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data) { const QPointF &startPoint = path.currentPosition(); @@ -1806,7 +1842,7 @@ void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data) QQuickSvgParser::pathArc(path, _radiusX, _radiusY, - 0, //xAxisRotation + _xAxisRotation, _useLargeArc, _direction == Clockwise ? 1 : 0, endPoint.x(), diff --git a/src/quick/util/qquickpath_p.h b/src/quick/util/qquickpath_p.h index 283283f377..4bcefb032e 100644 --- a/src/quick/util/qquickpath_p.h +++ b/src/quick/util/qquickpath_p.h @@ -290,10 +290,11 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPathArc : public QQuickCurve Q_PROPERTY(qreal radiusY READ radiusY WRITE setRadiusY NOTIFY radiusYChanged) Q_PROPERTY(bool useLargeArc READ useLargeArc WRITE setUseLargeArc NOTIFY useLargeArcChanged) Q_PROPERTY(ArcDirection direction READ direction WRITE setDirection NOTIFY directionChanged) + Q_PROPERTY(qreal xAxisRotation READ xAxisRotation WRITE setXAxisRotation NOTIFY xAxisRotationChanged REVISION 2) public: QQuickPathArc(QObject *parent=0) - : QQuickCurve(parent), _radiusX(0), _radiusY(0), _useLargeArc(false), _direction(Clockwise) {} + : QQuickCurve(parent), _radiusX(0), _radiusY(0), _useLargeArc(false), _direction(Clockwise), _xAxisRotation(0) {} enum ArcDirection { Clockwise, Counterclockwise }; Q_ENUM(ArcDirection) @@ -310,6 +311,9 @@ public: ArcDirection direction() const; void setDirection(ArcDirection direction); + qreal xAxisRotation() const; + void setXAxisRotation(qreal rotation); + void addToPath(QPainterPath &path, const QQuickPathData &) override; Q_SIGNALS: @@ -317,12 +321,14 @@ Q_SIGNALS: void radiusYChanged(); void useLargeArcChanged(); void directionChanged(); + Q_REVISION(2) void xAxisRotationChanged(); private: qreal _radiusX; qreal _radiusY; bool _useLargeArc; ArcDirection _direction; + qreal _xAxisRotation; }; class Q_QUICK_PRIVATE_EXPORT QQuickPathSvg : public QQuickCurve -- cgit v1.2.3 From 7599fbffbf8ad88fa4487cd7d8bad90eb0b2d952 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 4 Jan 2017 14:34:51 +0100 Subject: NVPR: Take X axis rotation into account for PathArc Change-Id: I70238e0e4b458ddfde9f32c40b76382c1a183e98 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemnvprrenderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitemnvprrenderer.cpp b/src/quick/items/qquickpathitemnvprrenderer.cpp index bd88023891..5641d33f85 100644 --- a/src/quick/items/qquickpathitemnvprrenderer.cpp +++ b/src/quick/items/qquickpathitemnvprrenderer.cpp @@ -242,7 +242,7 @@ void QQuickPathItemNvprRenderer::convertPath(const QQuickPath *path) m_path.cmd.append(cmd); m_path.coord.append(o->radiusX()); m_path.coord.append(o->radiusY()); - m_path.coord.append(0.0f); // X axis rotation + m_path.coord.append(o->xAxisRotation()); appendCoords(&m_path.coord, o, &pos); } else { qWarning() << "PathItem/NVPR: unsupported Path element" << e; -- cgit v1.2.3 From 0271da9ff4001d26596a9172329691674e147ada Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 4 Jan 2017 15:12:32 +0100 Subject: Allow multiple paths in a PathItem Instead of PathItem { item properties stroke/fill properties path: Path { ... } } switch to PathItem { item properties VisualPath { stroke/fill settings Path { ... } } VisualPath { stroke/fill settings Path { ... } } ... } Limiting PathItem to a single path is arguably too limited. Applications will likely try to work this around by using multiple PathItems. While this is not particularly bad for the generic (geometry node based) implementation, it is a massive overkill for the rendernode-based ones. Therefore, avoid the hassle and allow multiple paths with different stroke/fill parameters inside a single PathItem. Change-Id: Ie7980cd656deb7d4cb1ee4eaa3c090c4b0493c7d Reviewed-by: Andy Nichols --- src/quick/items/qquickitemsmodule.cpp | 1 + src/quick/items/qquickpathitem.cpp | 460 +++++++++++------- src/quick/items/qquickpathitem_p.h | 70 ++- src/quick/items/qquickpathitem_p_p.h | 68 ++- src/quick/items/qquickpathitemgenericrenderer.cpp | 358 +++++++------- src/quick/items/qquickpathitemgenericrenderer_p.h | 105 ++--- src/quick/items/qquickpathitemnvprrenderer.cpp | 519 ++++++++++++--------- src/quick/items/qquickpathitemnvprrenderer_p.h | 108 +++-- src/quick/items/qquickpathitemsoftwarerenderer.cpp | 162 ++++--- src/quick/items/qquickpathitemsoftwarerenderer_p.h | 57 +-- src/quick/util/qquickpath.cpp | 73 +++ 11 files changed, 1158 insertions(+), 823 deletions(-) (limited to 'src') 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(uri, 2, 9, "PathArc"); qmlRegisterType(uri, 2, 9, "PathMove"); qmlRegisterType(uri, 2, 9, "PathItem"); + qmlRegisterType(uri, 2, 9, "VisualPath"); qmlRegisterType(uri, 2, 9, "PathGradientStop"); qmlRegisterUncreatableType(uri, 2, 9, "PathGradient", QQuickPathGradient::tr("PathGradient is an abstract base class")); qmlRegisterType(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 QQuickPathItem::dashPattern() const +QVector QQuickVisualPath::dashPattern() const { - Q_D(const QQuickPathItem); + Q_D(const QQuickVisualPath); return d->dashPattern; } -void QQuickPathItem::setDashPattern(const QVector &array) +void QQuickVisualPath::setDashPattern(const QVector &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 *property, int index) +{ + QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast(property->object)); + return d->vp.at(index); +} + +static void vpe_append(QQmlListProperty *property, QQuickVisualPath *obj) +{ + QQuickPathItem *item = static_cast(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 *property) +{ + QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast(property->object)); + return d->vp.count(); +} + +static void vpe_clear(QQmlListProperty *property) +{ + QQuickPathItem *item = static_cast(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 QQuickPathItem::visualPaths() +{ + return QQmlListProperty(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(renderer)->setNode( static_cast(node)); } else { - node = new QQuickPathItemGenericRootRenderNode(q->window(), hasFill, hasStroke); + node = new QQuickPathItemGenericNode; static_cast(renderer)->setRootNode( - static_cast(node)); + static_cast(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 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 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 &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 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 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 &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(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(m_fillVertices.data()); + d->fillVertices.resize(vertexCount); + ColoredVertex *vdst = reinterpret_cast(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(m_strokeVertices.data()); + d->strokeVertices.resize(vertexCount); + ColoredVertex *vdst = reinterpret_cast(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(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(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(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 &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 fillVertices; + QVector fillIndices; + QVector 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 m_fillVertices; - QVector m_fillIndices; - QVector m_strokeVertices; - - int m_syncDirty; - int m_effectiveDirty; + QVector 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 m_solidColorMaterial; QScopedPointer 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 &dashPattern) +void QQuickPathItemNvprRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &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(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 *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(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(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(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(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(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(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 &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 cmd; QVector 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 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 m_dashPattern; - bool m_fillGradientActive; - QQuickPathItemGradientCache::GradientDesc m_fillGradient; + int m_accDirty = 0; + + QVector 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 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 m_dashPattern; - bool m_fillGradientActive; - QQuickPathItemGradientCache::GradientDesc m_fillGradient; - QOpenGLFramebufferObject *m_fallbackFbo = nullptr; QQuickNvprBlitter m_fallbackBlitter; QOpenGLExtraFunctions *f = nullptr; + QVector 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 &dashPattern) +void QQuickPathItemSoftwareRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &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(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 &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 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 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 &) -- cgit v1.2.3 From d57ede19dff1c45840f0d60881cada194330a6f3 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 10 Jan 2017 14:08:31 +0100 Subject: PathItem/NVPR: Remove unused member variable Change-Id: I8228b39d01097179e8b969b2ed479c18b80e9759 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitem.cpp | 2 +- src/quick/items/qquickpathitemnvprrenderer_p.h | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp index 0dce376945..d7131b3833 100644 --- a/src/quick/items/qquickpathitem.cpp +++ b/src/quick/items/qquickpathitem.cpp @@ -520,7 +520,7 @@ void QQuickPathItemPrivate::createRenderer() case QSGRendererInterface::OpenGL: if (QQuickPathItemNvprRenderNode::isSupported()) { rendererType = QQuickPathItem::NvprRenderer; - renderer = new QQuickPathItemNvprRenderer(q); + renderer = new QQuickPathItemNvprRenderer; } else { rendererType = QQuickPathItem::GeometryRenderer; renderer = new QQuickPathItemGenericRenderer(q); diff --git a/src/quick/items/qquickpathitemnvprrenderer_p.h b/src/quick/items/qquickpathitemnvprrenderer_p.h index f594609e83..067ecf7355 100644 --- a/src/quick/items/qquickpathitemnvprrenderer_p.h +++ b/src/quick/items/qquickpathitemnvprrenderer_p.h @@ -79,10 +79,6 @@ public: DirtyList = 0x20 }; - QQuickPathItemNvprRenderer(QQuickItem *item) - : m_item(item) - { } - void beginSync(int totalCount) override; void setPath(int index, const QQuickPath *path) override; void setStrokeColor(int index, const QColor &color) override; @@ -126,7 +122,6 @@ private: void convertPath(const QQuickPath *path, VisualPathGuiData *d); - QQuickItem *m_item; QQuickPathItemNvprRenderNode *m_node = nullptr; int m_accDirty = 0; -- cgit v1.2.3 From e0200d5a72d29a3918bf66290e498f99c0a62a0a Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Sun, 8 Jan 2017 14:47:27 +0100 Subject: Clean up pathitem example grid view Combine stroke-only and fill-only. Drop the Repeater since it shows the bad practice of unnecessarily creating multiple PathItem instances. Drop the PathSvg example for now since this is a discouraged element. Move the tiger into a separate file and use an async Loader in order to avoid the 2 step activation. Change-Id: Ie65eb0284bdb8957f8fd5af7557542a6d044ef61 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemgenericrenderer.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp index b42ff1d4b0..dbbc14d3c8 100644 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -73,6 +73,9 @@ QQuickPathItemGenericStrokeFillNode::QQuickPathItemGenericStrokeFillNode(QQuickW { setGeometry(&m_geometry); activateMaterial(MatSolidColor); +#ifdef QSG_RUNTIME_DESCRIPTION + qsgnode_set_description(this, QLatin1String("stroke-fill")); +#endif } void QQuickPathItemGenericStrokeFillNode::activateMaterial(Material m) -- cgit v1.2.3 From f02e234fc012f4430378bc5205f32914822e4dff Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Sun, 8 Jan 2017 16:08:34 +0100 Subject: Reduce state changes with NVPR-backed PathItems Not setting the matrices and other states again and again for every path in the item saves a little CPU time. Change-Id: Iec9447de4c22cd25343462160bd4a8b9aa44368a Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemnvprrenderer.cpp | 37 +++++++++++++++++--------- 1 file changed, 25 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitemnvprrenderer.cpp b/src/quick/items/qquickpathitemnvprrenderer.cpp index 2338e51ff8..0c4357145a 100644 --- a/src/quick/items/qquickpathitemnvprrenderer.cpp +++ b/src/quick/items/qquickpathitemnvprrenderer.cpp @@ -585,6 +585,7 @@ void QQuickPathItemNvprRenderNode::renderOffscreenFill(VisualPathRenderData *d) return; f->glViewport(0, 0, itemSize.width(), itemSize.height()); + f->glDisable(GL_DEPTH_TEST); f->glClearColor(0, 0, 0, 0); f->glClearStencil(0); f->glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); @@ -599,6 +600,7 @@ void QQuickPathItemNvprRenderNode::renderOffscreenFill(VisualPathRenderData *d) renderFill(d); d->fallbackFbo->release(); + f->glEnable(GL_DEPTH_TEST); f->glViewport(0, 0, rtSize.width(), rtSize.height()); } @@ -635,6 +637,20 @@ void QQuickPathItemNvprRenderNode::render(const RenderState *state) const bool stencilClip = state->stencilEnabled(); // when true, the stencil buffer already has a clip path with a ref value of sv const int sv = state->stencilValue(); + const bool hasScissor = state->scissorEnabled(); + + if (hasScissor) { + // scissor rect is already set, just enable scissoring + f->glEnable(GL_SCISSOR_TEST); + } + + // 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); + + bool reloadMatrices = true; for (VisualPathRenderData &d : m_vp) { updatePath(&d); @@ -646,21 +662,18 @@ void QQuickPathItemNvprRenderNode::render(const RenderState *state) // 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... + if (hasScissor) + f->glDisable(GL_SCISSOR_TEST); renderOffscreenFill(&d); + reloadMatrices = true; + if (hasScissor) + f->glEnable(GL_SCISSOR_TEST); } - // Depth test against the opaque batches rendered before. - f->glEnable(GL_DEPTH_TEST); - f->glDepthFunc(GL_LESS); - nvpr.pathCoverDepthFunc(GL_LESS); - nvpr.pathStencilDepthOffset(-0.05f, -1); - - nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, matrix()->constData()); - nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, state->projectionMatrix()->constData()); - - if (state->scissorEnabled()) { - // scissor rect is already set, just enable scissoring - f->glEnable(GL_SCISSOR_TEST); + if (reloadMatrices) { + reloadMatrices = false; + nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, matrix()->constData()); + nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, state->projectionMatrix()->constData()); } // Fill! -- cgit v1.2.3 From 5575226f6b25bcf507c3412677756e07ce671cef Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 9 Jan 2017 11:43:31 +0100 Subject: PathItem: add optional threaded triangulation Spending 300+ ms in polish for the tiger when running with the generic backend is not going to fly. Therefore, add an optional mode for asynchronous triangulation. Change-Id: Ida44c7b8a28d38fb11243a6657221f039c62e21b Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitem.cpp | 20 ++- src/quick/items/qquickpathitem_p.h | 5 + src/quick/items/qquickpathitem_p_p.h | 3 +- src/quick/items/qquickpathitemgenericrenderer.cpp | 197 ++++++++++++++++----- src/quick/items/qquickpathitemgenericrenderer_p.h | 70 ++++++-- src/quick/items/qquickpathitemnvprrenderer.cpp | 2 +- src/quick/items/qquickpathitemnvprrenderer_p.h | 2 +- src/quick/items/qquickpathitemsoftwarerenderer.cpp | 2 +- src/quick/items/qquickpathitemsoftwarerenderer_p.h | 2 +- 9 files changed, 243 insertions(+), 60 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp index d7131b3833..964d997806 100644 --- a/src/quick/items/qquickpathitem.cpp +++ b/src/quick/items/qquickpathitem.cpp @@ -361,6 +361,7 @@ QQuickPathItemPrivate::QQuickPathItemPrivate() : componentComplete(true), vpChanged(false), rendererType(QQuickPathItem::UnknownRenderer), + async(false), renderer(nullptr) { } @@ -393,6 +394,23 @@ QQuickPathItem::RendererType QQuickPathItem::rendererType() const return d->rendererType; } +bool QQuickPathItem::asynchronous() const +{ + Q_D(const QQuickPathItem); + return d->async; +} + +void QQuickPathItem::setAsynchronous(bool async) +{ + Q_D(QQuickPathItem); + if (d->async != async) { + d->async = async; + emit asynchronousChanged(); + if (d->componentComplete) + d->_q_visualPathChanged(); + } +} + static QQuickVisualPath *vpe_at(QQmlListProperty *property, int index) { QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast(property->object)); @@ -606,7 +624,7 @@ void QQuickPathItemPrivate::sync() dirty = 0; } - renderer->endSync(); + renderer->endSync(async); } // ***** gradient support ***** diff --git a/src/quick/items/qquickpathitem_p.h b/src/quick/items/qquickpathitem_p.h index 0c1d0f061b..7c962d01fc 100644 --- a/src/quick/items/qquickpathitem_p.h +++ b/src/quick/items/qquickpathitem_p.h @@ -263,6 +263,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPathItem : public QQuickItem { Q_OBJECT Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged) + Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) Q_PROPERTY(QQmlListProperty visualPaths READ visualPaths) Q_CLASSINFO("DefaultProperty", "visualPaths") @@ -280,6 +281,9 @@ public: RendererType rendererType() const; + bool asynchronous() const; + void setAsynchronous(bool async); + QQmlListProperty visualPaths(); protected: @@ -291,6 +295,7 @@ protected: Q_SIGNALS: void rendererChanged(); + void asynchronousChanged(); private: Q_DISABLE_COPY(QQuickPathItem) diff --git a/src/quick/items/qquickpathitem_p_p.h b/src/quick/items/qquickpathitem_p_p.h index 5e6400edc6..faf7b1e451 100644 --- a/src/quick/items/qquickpathitem_p_p.h +++ b/src/quick/items/qquickpathitem_p_p.h @@ -79,7 +79,7 @@ public: virtual void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, qreal dashOffset, const QVector &dashPattern) = 0; virtual void setFillGradient(int index, QQuickPathGradient *gradient) = 0; - virtual void endSync() = 0; + virtual void endSync(bool async) = 0; // Render thread, with gui blocked virtual void updateNode() = 0; @@ -144,6 +144,7 @@ public: bool componentComplete; bool vpChanged; QQuickPathItem::RendererType rendererType; + bool async; QQuickAbstractPathRenderer *renderer; QVector vp; }; diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp index dbbc14d3c8..6e4c89742a 100644 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -39,7 +39,9 @@ #include "qquickpathitemgenericrenderer_p.h" #include +#include #include +#include QT_BEGIN_NAMESPACE @@ -103,6 +105,8 @@ void QQuickPathItemGenericStrokeFillNode::activateMaterial(Material m) } // sync, and so triangulation too, happens on the gui thread +// - except when async is set, in which case triangulation is moved to worker threads + void QQuickPathItemGenericRenderer::beginSync(int totalCount) { if (m_vp.count() != totalCount) { @@ -194,9 +198,22 @@ void QQuickPathItemGenericRenderer::setFillGradient(int index, QQuickPathGradien d.syncDirty |= DirtyFillGradient; } -void QQuickPathItemGenericRenderer::endSync() +void QQuickPathItemFillRunnable::run() { - for (VisualPathData &d : m_vp) { + QQuickPathItemGenericRenderer::triangulateFill(path, fillColor, &fillVertices, &fillIndices); + emit done(this); +} + +void QQuickPathItemStrokeRunnable::run() +{ + QQuickPathItemGenericRenderer::triangulateStroke(path, pen, strokeColor, &strokeVertices, clipSize); + emit done(this); +} + +void QQuickPathItemGenericRenderer::endSync(bool async) +{ + for (int i = 0; i < m_vp.count(); ++i) { + VisualPathData &d(m_vp[i]); if (!d.syncDirty) continue; @@ -217,67 +234,145 @@ void QQuickPathItemGenericRenderer::endSync() } if (d.syncDirty & DirtyGeom) { - if (d.fillColor.a) - triangulateFill(&d); - if (d.strokeWidth >= 0.0f && d.strokeColor.a) - triangulateStroke(&d); + static QThreadPool threadPool; + static bool threadPoolReady = false; + if (async && !threadPoolReady) { + threadPoolReady = true; + const int idealCount = QThread::idealThreadCount(); + threadPool.setMaxThreadCount(idealCount > 0 ? idealCount * 2 : 4); + } + if (d.fillColor.a) { + d.path.setFillRule(d.fillRule); + if (async) { + QQuickPathItemFillRunnable *r = new QQuickPathItemFillRunnable; + r->setAutoDelete(false); + if (d.pendingFill) + d.pendingFill->orphaned = true; + d.pendingFill = r; + r->path = d.path; + r->fillColor = d.fillColor; + // Unlikely in practice but in theory m_vp could be + // resized. Therefore, capture 'i' instead of 'd'. + QObject::connect(r, &QQuickPathItemFillRunnable::done, qApp, [this, i](QQuickPathItemFillRunnable *r) { + if (!r->orphaned && i < m_vp.count()) { + VisualPathData &d(m_vp[i]); + d.fillVertices = r->fillVertices; + d.fillIndices = r->fillIndices; + d.pendingFill = nullptr; + d.effectiveDirty |= DirtyGeom; + maybeUpdateAsyncItem(); + } + r->deleteLater(); + }); + threadPool.start(r); + } else { + triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices); + } + } + if (d.strokeWidth >= 0.0f && d.strokeColor.a) { + if (async) { + QQuickPathItemStrokeRunnable *r = new QQuickPathItemStrokeRunnable; + r->setAutoDelete(false); + if (d.pendingStroke) + d.pendingStroke->orphaned = true; + d.pendingStroke = r; + r->path = d.path; + r->pen = d.pen; + r->strokeColor = d.strokeColor; + r->clipSize = QSize(m_item->width(), m_item->height()); + QObject::connect(r, &QQuickPathItemStrokeRunnable::done, qApp, [this, i](QQuickPathItemStrokeRunnable *r) { + if (!r->orphaned && i < m_vp.count()) { + VisualPathData &d(m_vp[i]); + d.strokeVertices = r->strokeVertices; + d.pendingStroke = nullptr; + d.effectiveDirty |= DirtyGeom; + maybeUpdateAsyncItem(); + } + r->deleteLater(); + }); + threadPool.start(r); + } else { + triangulateStroke(d.path, d.pen, d.strokeColor, &d.strokeVertices, + QSize(m_item->width(), m_item->height())); + } + } } } } -void QQuickPathItemGenericRenderer::triangulateFill(VisualPathData *d) +void QQuickPathItemGenericRenderer::maybeUpdateAsyncItem() { - d->path.setFillRule(d->fillRule); + for (const VisualPathData &d : qAsConst(m_vp)) { + if (d.pendingFill || d.pendingStroke) + return; + } + m_accDirty |= DirtyGeom; + m_item->update(); +} - const QVectorPath &vp = qtVectorPathForPath(d->path); +// the stroke/fill triangulation functions may be invoked either on the gui +// thread or some worker thread and must thus be self-contained. +void QQuickPathItemGenericRenderer::triangulateFill(const QPainterPath &path, + const Color4ub &fillColor, + VerticesType *fillVertices, + IndicesType *fillIndices) +{ + const QVectorPath &vp = qtVectorPathForPath(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 - d->fillVertices.resize(vertexCount); - ColoredVertex *vdst = reinterpret_cast(d->fillVertices.data()); + fillVertices->resize(vertexCount); + ColoredVertex *vdst = reinterpret_cast(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, d->fillColor); + vdst[i].set(vsrc[i * 2] / SCALE, vsrc[i * 2 + 1] / SCALE, fillColor); - d->fillIndices.resize(ts.indices.size()); - quint16 *idst = d->fillIndices.data(); + fillIndices->resize(ts.indices.size()); + quint16 *idst = fillIndices->data(); if (ts.indices.type() == QVertexIndexVector::UnsignedShort) { - memcpy(idst, ts.indices.data(), d->fillIndices.count() * sizeof(quint16)); + memcpy(idst, ts.indices.data(), fillIndices->count() * sizeof(quint16)); } else { const quint32 *isrc = (const quint32 *) ts.indices.data(); - for (int i = 0; i < d->fillIndices.count(); ++i) + for (int i = 0; i < fillIndices->count(); ++i) idst[i] = isrc[i]; } } -void QQuickPathItemGenericRenderer::triangulateStroke(VisualPathData *d) +void QQuickPathItemGenericRenderer::triangulateStroke(const QPainterPath &path, + const QPen &pen, + const Color4ub &strokeColor, + VerticesType *strokeVertices, + const QSize &clipSize) { - const QVectorPath &vp = qtVectorPathForPath(d->path); - - const QRectF clip(0, 0, m_item->width(), m_item->height()); + const QVectorPath &vp = qtVectorPathForPath(path); + const QRectF clip(QPointF(0, 0), clipSize); const qreal inverseScale = 1.0 / SCALE; - m_stroker.setInvScale(inverseScale); - if (d->pen.style() == Qt::SolidLine) { - m_stroker.process(vp, d->pen, clip, 0); + + QTriangulatingStroker stroker; + stroker.setInvScale(inverseScale); + + if (pen.style() == Qt::SolidLine) { + stroker.process(vp, pen, clip, 0); } else { - m_dashStroker.setInvScale(inverseScale); - m_dashStroker.process(vp, d->pen, clip, 0); - QVectorPath dashStroke(m_dashStroker.points(), m_dashStroker.elementCount(), - m_dashStroker.elementTypes(), 0); - m_stroker.process(dashStroke, d->pen, clip, 0); + QDashedStrokeProcessor dashStroker; + dashStroker.setInvScale(inverseScale); + dashStroker.process(vp, pen, clip, 0); + QVectorPath dashStroke(dashStroker.points(), dashStroker.elementCount(), + dashStroker.elementTypes(), 0); + stroker.process(dashStroke, pen, clip, 0); } - if (!m_stroker.vertexCount()) { - d->strokeVertices.clear(); + if (!stroker.vertexCount()) { + strokeVertices->clear(); return; } - const int vertexCount = m_stroker.vertexCount() / 2; // just a float vector with x,y hence the / 2 - d->strokeVertices.resize(vertexCount); - ColoredVertex *vdst = reinterpret_cast(d->strokeVertices.data()); - const float *vsrc = m_stroker.vertices(); + const int vertexCount = stroker.vertexCount() / 2; // just a float vector with x,y hence the / 2 + strokeVertices->resize(vertexCount); + ColoredVertex *vdst = reinterpret_cast(strokeVertices->data()); + const float *vsrc = stroker.vertices(); for (int i = 0; i < vertexCount; ++i) - vdst[i].set(vsrc[i * 2], vsrc[i * 2 + 1], d->strokeColor); + vdst[i].set(vsrc[i * 2], vsrc[i * 2 + 1], strokeColor); } void QQuickPathItemGenericRenderer::setRootNode(QQuickPathItemGenericNode *node) @@ -317,7 +412,7 @@ void QQuickPathItemGenericRenderer::updateNode() QQuickPathItemGenericNode *node = *nodePtr; if (m_accDirty & DirtyList) - d.effectiveDirty |= DirtyGeom; + d.effectiveDirty |= DirtyGeom | DirtyColor | DirtyFillGradient; if (!d.effectiveDirty) continue; @@ -361,25 +456,36 @@ void QQuickPathItemGenericRenderer::updateNode() m_accDirty = 0; } +void QQuickPathItemGenericRenderer::updateShadowDataInNode(VisualPathData *d, QQuickPathItemGenericStrokeFillNode *n) +{ + if (d->fillGradientActive) { + if (d->effectiveDirty & DirtyFillGradient) + n->m_fillGradient = d->fillGradient; + } +} + void QQuickPathItemGenericRenderer::updateFillNode(VisualPathData *d, QQuickPathItemGenericNode *node) { if (!node->m_fillNode) return; + // Make a copy of the data that will be accessed by the material on + // the render thread. This must be done even when we bail out below. QQuickPathItemGenericStrokeFillNode *n = node->m_fillNode; + updateShadowDataInNode(d, n); + QSGGeometry *g = &n->m_geometry; if (d->fillVertices.isEmpty()) { - g->allocate(0, 0); - n->markDirty(QSGNode::DirtyGeometry); + if (g->vertexCount() || g->indexCount()) { + g->allocate(0, 0); + n->markDirty(QSGNode::DirtyGeometry); + } return; } 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 = 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 @@ -414,14 +520,17 @@ void QQuickPathItemGenericRenderer::updateStrokeNode(VisualPathData *d, QQuickPa return; QQuickPathItemGenericStrokeFillNode *n = node->m_strokeNode; - n->markDirty(QSGNode::DirtyGeometry); - QSGGeometry *g = &n->m_geometry; if (d->strokeVertices.isEmpty()) { - g->allocate(0, 0); + if (g->vertexCount() || g->indexCount()) { + g->allocate(0, 0); + n->markDirty(QSGNode::DirtyGeometry); + } return; } + n->markDirty(QSGNode::DirtyGeometry); + if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyGeom)) { ColoredVertex *vdst = reinterpret_cast(g->vertexData()); for (int i = 0; i < g->vertexCount(); ++i) diff --git a/src/quick/items/qquickpathitemgenericrenderer_p.h b/src/quick/items/qquickpathitemgenericrenderer_p.h index 4454b14a13..a4ed090004 100644 --- a/src/quick/items/qquickpathitemgenericrenderer_p.h +++ b/src/quick/items/qquickpathitemgenericrenderer_p.h @@ -55,11 +55,14 @@ #include #include #include -#include +#include QT_BEGIN_NAMESPACE class QQuickPathItemGenericNode; +class QQuickPathItemGenericStrokeFillNode; +class QQuickPathItemFillRunnable; +class QQuickPathItemStrokeRunnable; class QQuickPathItemGenericRenderer : public QQuickAbstractPathRenderer { @@ -88,15 +91,29 @@ public: void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, qreal dashOffset, const QVector &dashPattern) override; void setFillGradient(int index, QQuickPathGradient *gradient) override; - void endSync() override; + void endSync(bool async) override; void updateNode() override; void setRootNode(QQuickPathItemGenericNode *node); struct Color4ub { unsigned char r, g, b, a; }; + typedef QVector VerticesType; + typedef QVector IndicesType; + + static void triangulateFill(const QPainterPath &path, + const Color4ub &fillColor, + VerticesType *fillVertices, + IndicesType *fillIndices); + static void triangulateStroke(const QPainterPath &path, + const QPen &pen, + const Color4ub &strokeColor, + VerticesType *strokeVertices, + const QSize &clipSize); private: + void maybeUpdateAsyncItem(); + struct VisualPathData { float strokeWidth; QPen pen; @@ -106,27 +123,60 @@ private: QPainterPath path; bool fillGradientActive; QQuickPathItemGradientCache::GradientDesc fillGradient; - QVector fillVertices; - QVector fillIndices; - QVector strokeVertices; + VerticesType fillVertices; + IndicesType fillIndices; + VerticesType strokeVertices; int syncDirty; int effectiveDirty = 0; + QQuickPathItemFillRunnable *pendingFill = nullptr; + QQuickPathItemStrokeRunnable *pendingStroke = nullptr; }; - void triangulateFill(VisualPathData *d); - void triangulateStroke(VisualPathData *d); - + void updateShadowDataInNode(VisualPathData *d, QQuickPathItemGenericStrokeFillNode *n); void updateFillNode(VisualPathData *d, QQuickPathItemGenericNode *node); void updateStrokeNode(VisualPathData *d, QQuickPathItemGenericNode *node); QQuickItem *m_item; QQuickPathItemGenericNode *m_rootNode; - QTriangulatingStroker m_stroker; - QDashedStrokeProcessor m_dashStroker; QVector m_vp; int m_accDirty; }; +class QQuickPathItemFillRunnable : public QObject, public QRunnable +{ + Q_OBJECT + +public: + void run() override; + + bool orphaned = false; + QPainterPath path; + QQuickPathItemGenericRenderer::Color4ub fillColor; + QQuickPathItemGenericRenderer::VerticesType fillVertices; + QQuickPathItemGenericRenderer::IndicesType fillIndices; + +Q_SIGNALS: + void done(QQuickPathItemFillRunnable *self); +}; + +class QQuickPathItemStrokeRunnable : public QObject, public QRunnable +{ + Q_OBJECT + +public: + void run() override; + + bool orphaned = false; + QPainterPath path; + QPen pen; + QQuickPathItemGenericRenderer::Color4ub strokeColor; + QQuickPathItemGenericRenderer::VerticesType strokeVertices; + QSize clipSize; + +Q_SIGNALS: + void done(QQuickPathItemStrokeRunnable *self); +}; + class QQuickPathItemGenericStrokeFillNode : public QSGGeometryNode { public: diff --git a/src/quick/items/qquickpathitemnvprrenderer.cpp b/src/quick/items/qquickpathitemnvprrenderer.cpp index 0c4357145a..9303f698ac 100644 --- a/src/quick/items/qquickpathitemnvprrenderer.cpp +++ b/src/quick/items/qquickpathitemnvprrenderer.cpp @@ -140,7 +140,7 @@ void QQuickPathItemNvprRenderer::setFillGradient(int index, QQuickPathGradient * m_accDirty |= DirtyFillGradient; } -void QQuickPathItemNvprRenderer::endSync() +void QQuickPathItemNvprRenderer::endSync(bool) { } diff --git a/src/quick/items/qquickpathitemnvprrenderer_p.h b/src/quick/items/qquickpathitemnvprrenderer_p.h index 067ecf7355..1617de17e6 100644 --- a/src/quick/items/qquickpathitemnvprrenderer_p.h +++ b/src/quick/items/qquickpathitemnvprrenderer_p.h @@ -90,7 +90,7 @@ public: void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, qreal dashOffset, const QVector &dashPattern) override; void setFillGradient(int index, QQuickPathGradient *gradient) override; - void endSync() override; + void endSync(bool async) override; void updateNode() override; diff --git a/src/quick/items/qquickpathitemsoftwarerenderer.cpp b/src/quick/items/qquickpathitemsoftwarerenderer.cpp index 6c3cf73a56..63d4f8f6d4 100644 --- a/src/quick/items/qquickpathitemsoftwarerenderer.cpp +++ b/src/quick/items/qquickpathitemsoftwarerenderer.cpp @@ -158,7 +158,7 @@ void QQuickPathItemSoftwareRenderer::setFillGradient(int index, QQuickPathGradie m_accDirty |= DirtyBrush; } -void QQuickPathItemSoftwareRenderer::endSync() +void QQuickPathItemSoftwareRenderer::endSync(bool) { } diff --git a/src/quick/items/qquickpathitemsoftwarerenderer_p.h b/src/quick/items/qquickpathitemsoftwarerenderer_p.h index 60b52e2def..38130d7301 100644 --- a/src/quick/items/qquickpathitemsoftwarerenderer_p.h +++ b/src/quick/items/qquickpathitemsoftwarerenderer_p.h @@ -82,7 +82,7 @@ public: void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, qreal dashOffset, const QVector &dashPattern) override; void setFillGradient(int index, QQuickPathGradient *gradient) override; - void endSync() override; + void endSync(bool async) override; void updateNode() override; -- cgit v1.2.3 From 72cfdac50b752b8f2d45929265cf4f09cb990bd2 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 10 Jan 2017 10:29:38 +0100 Subject: Add PathItem.status to report async processing progress Change-Id: I09ccdf9c542ebca56187284a0d1776b64c98bfe8 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitem.cpp | 38 +++++++++++++++++++++-- src/quick/items/qquickpathitem_p.h | 11 +++++++ src/quick/items/qquickpathitem_p_p.h | 11 +++++++ src/quick/items/qquickpathitemgenericrenderer.cpp | 15 +++++++++ src/quick/items/qquickpathitemgenericrenderer_p.h | 7 ++++- 5 files changed, 78 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp index 964d997806..306e79dc1e 100644 --- a/src/quick/items/qquickpathitem.cpp +++ b/src/quick/items/qquickpathitem.cpp @@ -362,6 +362,7 @@ QQuickPathItemPrivate::QQuickPathItemPrivate() vpChanged(false), rendererType(QQuickPathItem::UnknownRenderer), async(false), + status(QQuickPathItem::Null), renderer(nullptr) { } @@ -378,6 +379,15 @@ void QQuickPathItemPrivate::_q_visualPathChanged() q->polish(); } +void QQuickPathItemPrivate::setStatus(QQuickPathItem::Status newStatus) +{ + Q_Q(QQuickPathItem); + if (status != newStatus) { + status = newStatus; + emit q->statusChanged(); + } +} + QQuickPathItem::QQuickPathItem(QQuickItem *parent) : QQuickItem(*(new QQuickPathItemPrivate), parent) { @@ -411,6 +421,12 @@ void QQuickPathItem::setAsynchronous(bool async) } } +QQuickPathItem::Status QQuickPathItem::status() const +{ + Q_D(const QQuickPathItem); + return d->status; +} + static QQuickVisualPath *vpe_at(QQmlListProperty *property, int index) { QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast(property->object)); @@ -492,8 +508,9 @@ void QQuickPathItem::updatePolish() emit rendererChanged(); } - // endSync() is where expensive calculations may happen, depending on the - // backend. Therefore do this only when the item is visible. + // endSync() is where expensive calculations may happen (or get kicked off + // on worker threads), depending on the backend. Therefore do this only + // when the item is visible. if (isVisible()) d->sync(); @@ -593,8 +610,20 @@ QSGNode *QQuickPathItemPrivate::createNode() return node; } +static void q_asyncPathItemReady(void *data) +{ + QQuickPathItemPrivate *self = static_cast(data); + self->setStatus(QQuickPathItem::Ready); +} + void QQuickPathItemPrivate::sync() { + const bool useAsync = async && renderer->flags().testFlag(QQuickAbstractPathRenderer::SupportsAsync); + if (useAsync) { + setStatus(QQuickPathItem::Processing); + renderer->setAsyncCallback(q_asyncPathItemReady, this); + } + const int count = vp.count(); renderer->beginSync(count); @@ -624,7 +653,10 @@ void QQuickPathItemPrivate::sync() dirty = 0; } - renderer->endSync(async); + renderer->endSync(useAsync); + + if (!useAsync) + setStatus(QQuickPathItem::Ready); } // ***** gradient support ***** diff --git a/src/quick/items/qquickpathitem_p.h b/src/quick/items/qquickpathitem_p.h index 7c962d01fc..1b36348cd2 100644 --- a/src/quick/items/qquickpathitem_p.h +++ b/src/quick/items/qquickpathitem_p.h @@ -264,6 +264,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPathItem : public QQuickItem Q_OBJECT Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged) Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) Q_PROPERTY(QQmlListProperty visualPaths READ visualPaths) Q_CLASSINFO("DefaultProperty", "visualPaths") @@ -276,6 +277,13 @@ public: }; Q_ENUM(RendererType) + enum Status { + Null, + Ready, + Processing + }; + Q_ENUM(Status) + QQuickPathItem(QQuickItem *parent = nullptr); ~QQuickPathItem(); @@ -284,6 +292,8 @@ public: bool asynchronous() const; void setAsynchronous(bool async); + Status status() const; + QQmlListProperty visualPaths(); protected: @@ -296,6 +306,7 @@ protected: Q_SIGNALS: void rendererChanged(); void asynchronousChanged(); + void statusChanged(); private: Q_DISABLE_COPY(QQuickPathItem) diff --git a/src/quick/items/qquickpathitem_p_p.h b/src/quick/items/qquickpathitem_p_p.h index faf7b1e451..3c63ec6dc2 100644 --- a/src/quick/items/qquickpathitem_p_p.h +++ b/src/quick/items/qquickpathitem_p_p.h @@ -65,6 +65,11 @@ class QSGPlainTexture; class QQuickAbstractPathRenderer { public: + enum Flag { + SupportsAsync = 0x01 + }; + Q_DECLARE_FLAGS(Flags, Flag) + virtual ~QQuickAbstractPathRenderer() { } // Gui thread @@ -80,11 +85,15 @@ public: qreal dashOffset, const QVector &dashPattern) = 0; virtual void setFillGradient(int index, QQuickPathGradient *gradient) = 0; virtual void endSync(bool async) = 0; + virtual void setAsyncCallback(void (*)(void *), void *) { } + virtual Flags flags() const { return 0; } // Render thread, with gui blocked virtual void updateNode() = 0; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickAbstractPathRenderer::Flags) + class QQuickVisualPathPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QQuickVisualPath) @@ -138,6 +147,7 @@ public: void sync(); void _q_visualPathChanged(); + void setStatus(QQuickPathItem::Status newStatus); static QQuickPathItemPrivate *get(QQuickPathItem *item) { return item->d_func(); } @@ -145,6 +155,7 @@ public: bool vpChanged; QQuickPathItem::RendererType rendererType; bool async; + QQuickPathItem::Status status; QQuickAbstractPathRenderer *renderer; QVector vp; }; diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp index 6e4c89742a..59b03e0fd2 100644 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -210,8 +210,16 @@ void QQuickPathItemStrokeRunnable::run() emit done(this); } +void QQuickPathItemGenericRenderer::setAsyncCallback(void (*callback)(void *), void *data) +{ + m_asyncCallback = callback; + m_asyncCallbackData = data; +} + void QQuickPathItemGenericRenderer::endSync(bool async) { + bool didKickOffAsync = false; + for (int i = 0; i < m_vp.count(); ++i) { VisualPathData &d(m_vp[i]); if (!d.syncDirty) @@ -264,6 +272,7 @@ void QQuickPathItemGenericRenderer::endSync(bool async) } r->deleteLater(); }); + didKickOffAsync = true; threadPool.start(r); } else { triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices); @@ -290,6 +299,7 @@ void QQuickPathItemGenericRenderer::endSync(bool async) } r->deleteLater(); }); + didKickOffAsync = true; threadPool.start(r); } else { triangulateStroke(d.path, d.pen, d.strokeColor, &d.strokeVertices, @@ -298,6 +308,9 @@ void QQuickPathItemGenericRenderer::endSync(bool async) } } } + + if (!didKickOffAsync && async && m_asyncCallback) + m_asyncCallback(m_asyncCallbackData); } void QQuickPathItemGenericRenderer::maybeUpdateAsyncItem() @@ -308,6 +321,8 @@ void QQuickPathItemGenericRenderer::maybeUpdateAsyncItem() } m_accDirty |= DirtyGeom; m_item->update(); + if (m_asyncCallback) + m_asyncCallback(m_asyncCallbackData); } // the stroke/fill triangulation functions may be invoked either on the gui diff --git a/src/quick/items/qquickpathitemgenericrenderer_p.h b/src/quick/items/qquickpathitemgenericrenderer_p.h index a4ed090004..3193c27cb3 100644 --- a/src/quick/items/qquickpathitemgenericrenderer_p.h +++ b/src/quick/items/qquickpathitemgenericrenderer_p.h @@ -77,7 +77,8 @@ public: QQuickPathItemGenericRenderer(QQuickItem *item) : m_item(item), m_rootNode(nullptr), - m_accDirty(0) + m_accDirty(0), + m_asyncCallback(nullptr) { } void beginSync(int totalCount) override; @@ -92,6 +93,8 @@ public: qreal dashOffset, const QVector &dashPattern) override; void setFillGradient(int index, QQuickPathGradient *gradient) override; void endSync(bool async) override; + void setAsyncCallback(void (*)(void *), void *) override; + Flags flags() const override { return SupportsAsync; } void updateNode() override; @@ -140,6 +143,8 @@ private: QQuickPathItemGenericNode *m_rootNode; QVector m_vp; int m_accDirty; + void (*m_asyncCallback)(void *); + void *m_asyncCallbackData; }; class QQuickPathItemFillRunnable : public QObject, public QRunnable -- cgit v1.2.3 From 20c2c8b627b119b9ed72dd40f5b1f77786fad1de Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 10 Jan 2017 11:42:44 +0100 Subject: Separate fill and stroke geometry updates in generic PathItem A change in parameters affecting fill or stroke only does not need touching the other set of geometry at all. Split DirtyGeom into DirtyFillGeom and DirtyStrokeGeom. As a result, neither triangulation nor the QSGGeometry update will happen for fill when only a stroke-related parameter changes, and vice versa. Change-Id: I839873899d687b6c1c8541c9186c9d93438b999a Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemgenericrenderer.cpp | 157 +++++++++++----------- src/quick/items/qquickpathitemgenericrenderer_p.h | 9 +- 2 files changed, 85 insertions(+), 81 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp index 59b03e0fd2..367fac985e 100644 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -121,7 +121,7 @@ void QQuickPathItemGenericRenderer::setPath(int index, const QQuickPath *path) { VisualPathData &d(m_vp[index]); d.path = path ? path->path() : QPainterPath(); - d.syncDirty |= DirtyGeom; + d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom; } void QQuickPathItemGenericRenderer::setStrokeColor(int index, const QColor &color) @@ -137,7 +137,7 @@ void QQuickPathItemGenericRenderer::setStrokeWidth(int index, qreal w) d.strokeWidth = w; if (w >= 0.0f) d.pen.setWidthF(w); - d.syncDirty |= DirtyGeom; + d.syncDirty |= DirtyStrokeGeom; } void QQuickPathItemGenericRenderer::setFillColor(int index, const QColor &color) @@ -151,7 +151,7 @@ void QQuickPathItemGenericRenderer::setFillRule(int index, QQuickVisualPath::Fil { VisualPathData &d(m_vp[index]); d.fillRule = Qt::FillRule(fillRule); - d.syncDirty |= DirtyGeom; + d.syncDirty |= DirtyFillGeom; } void QQuickPathItemGenericRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) @@ -159,14 +159,14 @@ void QQuickPathItemGenericRenderer::setJoinStyle(int index, QQuickVisualPath::Jo VisualPathData &d(m_vp[index]); d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); d.pen.setMiterLimit(miterLimit); - d.syncDirty |= DirtyGeom; + d.syncDirty |= DirtyStrokeGeom; } void QQuickPathItemGenericRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) { VisualPathData &d(m_vp[index]); d.pen.setCapStyle(Qt::PenCapStyle(capStyle)); - d.syncDirty |= DirtyGeom; + d.syncDirty |= DirtyStrokeGeom; } void QQuickPathItemGenericRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, @@ -178,7 +178,7 @@ void QQuickPathItemGenericRenderer::setStrokeStyle(int index, QQuickVisualPath:: d.pen.setDashPattern(dashPattern); d.pen.setDashOffset(dashOffset); } - d.syncDirty |= DirtyGeom; + d.syncDirty |= DirtyStrokeGeom; } void QQuickPathItemGenericRenderer::setFillGradient(int index, QQuickPathGradient *gradient) @@ -241,70 +241,71 @@ void QQuickPathItemGenericRenderer::endSync(bool async) continue; } - if (d.syncDirty & DirtyGeom) { - static QThreadPool threadPool; - static bool threadPoolReady = false; - if (async && !threadPoolReady) { - threadPoolReady = true; - const int idealCount = QThread::idealThreadCount(); - threadPool.setMaxThreadCount(idealCount > 0 ? idealCount * 2 : 4); - } - if (d.fillColor.a) { - d.path.setFillRule(d.fillRule); - if (async) { - QQuickPathItemFillRunnable *r = new QQuickPathItemFillRunnable; - r->setAutoDelete(false); - if (d.pendingFill) - d.pendingFill->orphaned = true; - d.pendingFill = r; - r->path = d.path; - r->fillColor = d.fillColor; - // Unlikely in practice but in theory m_vp could be - // resized. Therefore, capture 'i' instead of 'd'. - QObject::connect(r, &QQuickPathItemFillRunnable::done, qApp, [this, i](QQuickPathItemFillRunnable *r) { - if (!r->orphaned && i < m_vp.count()) { - VisualPathData &d(m_vp[i]); - d.fillVertices = r->fillVertices; - d.fillIndices = r->fillIndices; - d.pendingFill = nullptr; - d.effectiveDirty |= DirtyGeom; - maybeUpdateAsyncItem(); - } - r->deleteLater(); - }); - didKickOffAsync = true; - threadPool.start(r); - } else { - triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices); - } + static QThreadPool threadPool; + static bool threadPoolReady = false; + + if (async && !threadPoolReady) { + threadPoolReady = true; + const int idealCount = QThread::idealThreadCount(); + threadPool.setMaxThreadCount(idealCount > 0 ? idealCount * 2 : 4); + } + + if ((d.syncDirty & DirtyFillGeom) && d.fillColor.a) { + d.path.setFillRule(d.fillRule); + if (async) { + QQuickPathItemFillRunnable *r = new QQuickPathItemFillRunnable; + r->setAutoDelete(false); + if (d.pendingFill) + d.pendingFill->orphaned = true; + d.pendingFill = r; + r->path = d.path; + r->fillColor = d.fillColor; + // Unlikely in practice but in theory m_vp could be + // resized. Therefore, capture 'i' instead of 'd'. + QObject::connect(r, &QQuickPathItemFillRunnable::done, qApp, [this, i](QQuickPathItemFillRunnable *r) { + if (!r->orphaned && i < m_vp.count()) { + VisualPathData &d(m_vp[i]); + d.fillVertices = r->fillVertices; + d.fillIndices = r->fillIndices; + d.pendingFill = nullptr; + d.effectiveDirty |= DirtyFillGeom; + maybeUpdateAsyncItem(); + } + r->deleteLater(); + }); + didKickOffAsync = true; + threadPool.start(r); + } else { + triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices); } - if (d.strokeWidth >= 0.0f && d.strokeColor.a) { - if (async) { - QQuickPathItemStrokeRunnable *r = new QQuickPathItemStrokeRunnable; - r->setAutoDelete(false); - if (d.pendingStroke) - d.pendingStroke->orphaned = true; - d.pendingStroke = r; - r->path = d.path; - r->pen = d.pen; - r->strokeColor = d.strokeColor; - r->clipSize = QSize(m_item->width(), m_item->height()); - QObject::connect(r, &QQuickPathItemStrokeRunnable::done, qApp, [this, i](QQuickPathItemStrokeRunnable *r) { - if (!r->orphaned && i < m_vp.count()) { - VisualPathData &d(m_vp[i]); - d.strokeVertices = r->strokeVertices; - d.pendingStroke = nullptr; - d.effectiveDirty |= DirtyGeom; - maybeUpdateAsyncItem(); - } - r->deleteLater(); - }); - didKickOffAsync = true; - threadPool.start(r); - } else { - triangulateStroke(d.path, d.pen, d.strokeColor, &d.strokeVertices, - QSize(m_item->width(), m_item->height())); - } + } + + if ((d.syncDirty & DirtyStrokeGeom) && d.strokeWidth >= 0.0f && d.strokeColor.a) { + if (async) { + QQuickPathItemStrokeRunnable *r = new QQuickPathItemStrokeRunnable; + r->setAutoDelete(false); + if (d.pendingStroke) + d.pendingStroke->orphaned = true; + d.pendingStroke = r; + r->path = d.path; + r->pen = d.pen; + r->strokeColor = d.strokeColor; + r->clipSize = QSize(m_item->width(), m_item->height()); + QObject::connect(r, &QQuickPathItemStrokeRunnable::done, qApp, [this, i](QQuickPathItemStrokeRunnable *r) { + if (!r->orphaned && i < m_vp.count()) { + VisualPathData &d(m_vp[i]); + d.strokeVertices = r->strokeVertices; + d.pendingStroke = nullptr; + d.effectiveDirty |= DirtyStrokeGeom; + maybeUpdateAsyncItem(); + } + r->deleteLater(); + }); + didKickOffAsync = true; + threadPool.start(r); + } else { + triangulateStroke(d.path, d.pen, d.strokeColor, &d.strokeVertices, + QSize(m_item->width(), m_item->height())); } } } @@ -319,7 +320,7 @@ void QQuickPathItemGenericRenderer::maybeUpdateAsyncItem() if (d.pendingFill || d.pendingStroke) return; } - m_accDirty |= DirtyGeom; + m_accDirty |= DirtyFillGeom | DirtyStrokeGeom; m_item->update(); if (m_asyncCallback) m_asyncCallback(m_asyncCallbackData); @@ -427,7 +428,7 @@ void QQuickPathItemGenericRenderer::updateNode() QQuickPathItemGenericNode *node = *nodePtr; if (m_accDirty & DirtyList) - d.effectiveDirty |= DirtyGeom | DirtyColor | DirtyFillGradient; + d.effectiveDirty |= DirtyFillGeom | DirtyStrokeGeom | DirtyColor | DirtyFillGradient; if (!d.effectiveDirty) continue; @@ -441,7 +442,7 @@ void QQuickPathItemGenericRenderer::updateNode() node->appendChildNode(node->m_fillNode); if (node->m_strokeNode) node->appendChildNode(node->m_strokeNode); - d.effectiveDirty |= DirtyGeom; + d.effectiveDirty |= DirtyFillGeom; } if (d.strokeWidth < 0.0f || d.strokeColor.a == 0) { @@ -450,7 +451,7 @@ void QQuickPathItemGenericRenderer::updateNode() } else if (!node->m_strokeNode) { node->m_strokeNode = new QQuickPathItemGenericStrokeFillNode(m_item->window()); node->appendChildNode(node->m_strokeNode); - d.effectiveDirty |= DirtyGeom; + d.effectiveDirty |= DirtyStrokeGeom; } updateFillNode(&d, node); @@ -483,6 +484,8 @@ void QQuickPathItemGenericRenderer::updateFillNode(VisualPathData *d, QQuickPath { if (!node->m_fillNode) return; + if (!(d->effectiveDirty & (DirtyFillGeom | DirtyColor | DirtyFillGradient))) + return; // Make a copy of the data that will be accessed by the material on // the render thread. This must be done even when we bail out below. @@ -504,13 +507,13 @@ void QQuickPathItemGenericRenderer::updateFillNode(VisualPathData *d, QQuickPath // 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 (!(d->effectiveDirty & DirtyGeom)) + if (!(d->effectiveDirty & DirtyFillGeom)) return; } } else { n->activateMaterial(QQuickPathItemGenericStrokeFillNode::MatSolidColor); // fast path for updating only color values when no change in vertex positions - if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyGeom)) { + if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyFillGeom)) { ColoredVertex *vdst = reinterpret_cast(g->vertexData()); for (int i = 0; i < g->vertexCount(); ++i) vdst[i].set(vdst[i].x, vdst[i].y, d->fillColor); @@ -531,7 +534,7 @@ void QQuickPathItemGenericRenderer::updateStrokeNode(VisualPathData *d, QQuickPa { if (!node->m_strokeNode) return; - if (d->effectiveDirty == DirtyFillGradient) // not applicable + if (!(d->effectiveDirty & (DirtyStrokeGeom | DirtyColor))) return; QQuickPathItemGenericStrokeFillNode *n = node->m_strokeNode; @@ -546,7 +549,7 @@ void QQuickPathItemGenericRenderer::updateStrokeNode(VisualPathData *d, QQuickPa n->markDirty(QSGNode::DirtyGeometry); - if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyGeom)) { + if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyStrokeGeom)) { ColoredVertex *vdst = reinterpret_cast(g->vertexData()); for (int i = 0; i < g->vertexCount(); ++i) vdst[i].set(vdst[i].x, vdst[i].y, d->strokeColor); diff --git a/src/quick/items/qquickpathitemgenericrenderer_p.h b/src/quick/items/qquickpathitemgenericrenderer_p.h index 3193c27cb3..17c1f73310 100644 --- a/src/quick/items/qquickpathitemgenericrenderer_p.h +++ b/src/quick/items/qquickpathitemgenericrenderer_p.h @@ -68,10 +68,11 @@ class QQuickPathItemGenericRenderer : public QQuickAbstractPathRenderer { public: enum Dirty { - DirtyGeom = 0x01, - DirtyColor = 0x02, - DirtyFillGradient = 0x04, - DirtyList = 0x08 + DirtyFillGeom = 0x01, + DirtyStrokeGeom = 0x02, + DirtyColor = 0x04, + DirtyFillGradient = 0x08, + DirtyList = 0x10 // only for accDirty }; QQuickPathItemGenericRenderer(QQuickItem *item) -- cgit v1.2.3 From 77614c6c48ce8caf5697afb53a26caf1b165fed3 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 10 Jan 2017 17:17:11 +0100 Subject: Correct software PathItem bounding rect Reporting the QQuickItem geometry is incorrect. Report the area touched by the path instead. This fixes the tiger in the pathitem example. Change-Id: Ib443f442411befabe7864eff6473604926527f4e Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemsoftwarerenderer.cpp | 9 ++++++++- src/quick/items/qquickpathitemsoftwarerenderer_p.h | 1 + src/quick/scenegraph/coreapi/qsgrendernode.cpp | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitemsoftwarerenderer.cpp b/src/quick/items/qquickpathitemsoftwarerenderer.cpp index 63d4f8f6d4..46ebcbfe6d 100644 --- a/src/quick/items/qquickpathitemsoftwarerenderer.cpp +++ b/src/quick/items/qquickpathitemsoftwarerenderer.cpp @@ -180,6 +180,8 @@ void QQuickPathItemSoftwareRenderer::updateNode() if (listChanged) m_node->m_vp.resize(count); + m_node->m_boundingRect = QRectF(); + for (int i = 0; i < count; ++i) { VisualPathGuiData &src(m_vp[i]); QQuickPathItemSoftwareRenderNode::VisualPathRenderData &dst(m_node->m_vp[i]); @@ -201,6 +203,11 @@ void QQuickPathItemSoftwareRenderer::updateNode() dst.brush = src.brush; src.dirty = 0; + + QRectF br = dst.path.boundingRect(); + const float sw = qMax(1.0f, dst.strokeWidth); + br.adjust(-sw, -sw, sw, sw); + m_node->m_boundingRect |= br; } m_node->markDirty(QSGNode::DirtyMaterial); @@ -256,7 +263,7 @@ QSGRenderNode::RenderingFlags QQuickPathItemSoftwareRenderNode::flags() const QRectF QQuickPathItemSoftwareRenderNode::rect() const { - return QRect(0, 0, m_item->width(), m_item->height()); + return m_boundingRect; } QT_END_NAMESPACE diff --git a/src/quick/items/qquickpathitemsoftwarerenderer_p.h b/src/quick/items/qquickpathitemsoftwarerenderer_p.h index 38130d7301..64280b436e 100644 --- a/src/quick/items/qquickpathitemsoftwarerenderer_p.h +++ b/src/quick/items/qquickpathitemsoftwarerenderer_p.h @@ -125,6 +125,7 @@ private: QBrush brush; }; QVector m_vp; + QRectF m_boundingRect; friend class QQuickPathItemSoftwareRenderer; }; diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.cpp b/src/quick/scenegraph/coreapi/qsgrendernode.cpp index 3007e0000c..a8954848d6 100644 --- a/src/quick/scenegraph/coreapi/qsgrendernode.cpp +++ b/src/quick/scenegraph/coreapi/qsgrendernode.cpp @@ -267,6 +267,10 @@ QSGRenderNode::RenderingFlags QSGRenderNode::flags() const For rendernodes covering the entire area of a corresponding QQuickItem the return value will be (0, 0, item->width(), item->height()). + \note Nodes are also free to render outside the boundaries specified by the + item's width and height, since the scenegraph nodes are not bounded by the + QQuickItem geometry, as long as this is reported correctly from this function. + \sa flags() */ QRectF QSGRenderNode::rect() const -- cgit v1.2.3 From a2252ba7b3942bbafbbb8f4db144f2cfbf0787ab Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Sun, 22 Jan 2017 16:48:15 +0100 Subject: Fix case when async PathItem goes down before finish Marking the work orphaned (to be ignored) is sufficient since runnables exist independently of the renderer or item instances. (even if the lambda executes after the entire Quick infra is gone, we are still fine, as long as we bail out in time and do not dereference the captured this) Change-Id: I0cfb09aaa8b363191ff576b17a88bb7c1c38f3ad Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemgenericrenderer.cpp | 12 ++++++++++++ src/quick/items/qquickpathitemgenericrenderer_p.h | 1 + 2 files changed, 13 insertions(+) (limited to 'src') diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp index 367fac985e..e36654ad0c 100644 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -104,6 +104,16 @@ void QQuickPathItemGenericStrokeFillNode::activateMaterial(Material m) setMaterial(m_material); } +QQuickPathItemGenericRenderer::~QQuickPathItemGenericRenderer() +{ + for (VisualPathData &d : m_vp) { + if (d.pendingFill) + d.pendingFill->orphaned = true; + if (d.pendingStroke) + d.pendingStroke->orphaned = true; + } +} + // sync, and so triangulation too, happens on the gui thread // - except when async is set, in which case triangulation is moved to worker threads @@ -263,6 +273,8 @@ void QQuickPathItemGenericRenderer::endSync(bool async) // Unlikely in practice but in theory m_vp could be // resized. Therefore, capture 'i' instead of 'd'. QObject::connect(r, &QQuickPathItemFillRunnable::done, qApp, [this, i](QQuickPathItemFillRunnable *r) { + // Bail out when orphaned (meaning either another run was + // started after this one, or the renderer got destroyed). if (!r->orphaned && i < m_vp.count()) { VisualPathData &d(m_vp[i]); d.fillVertices = r->fillVertices; diff --git a/src/quick/items/qquickpathitemgenericrenderer_p.h b/src/quick/items/qquickpathitemgenericrenderer_p.h index 17c1f73310..ca05492841 100644 --- a/src/quick/items/qquickpathitemgenericrenderer_p.h +++ b/src/quick/items/qquickpathitemgenericrenderer_p.h @@ -81,6 +81,7 @@ public: m_accDirty(0), m_asyncCallback(nullptr) { } + ~QQuickPathItemGenericRenderer(); void beginSync(int totalCount) override; void setPath(int index, const QQuickPath *path) override; -- cgit v1.2.3 From b9f060f6fae5734216d7088afd0e3b52165551c5 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 11 Jan 2017 12:49:33 +0100 Subject: Path generic PathItem work with both ushort and uint index type Default to uint but keep supporting systems without ElementIndexUint. Change-Id: Ic15f66408e2e2ffb6406c7e854fa65ee1e0f56b3 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemgenericrenderer.cpp | 100 ++++++++++++++++++---- src/quick/items/qquickpathitemgenericrenderer_p.h | 41 ++++++--- 2 files changed, 111 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp index e36654ad0c..bf9506ba65 100644 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -40,9 +40,15 @@ #include "qquickpathitemgenericrenderer_p.h" #include #include -#include #include +#ifndef QT_NO_OPENGL +#include +#include +#include +#include +#endif + QT_BEGIN_NAMESPACE static const qreal SCALE = 100; @@ -69,17 +75,22 @@ static inline QQuickPathItemGenericRenderer::Color4ub colorToColor4ub(const QCol } QQuickPathItemGenericStrokeFillNode::QQuickPathItemGenericStrokeFillNode(QQuickWindow *window) - : m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0, 0), + : m_geometry(new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0, 0)), m_window(window), m_material(nullptr) { - setGeometry(&m_geometry); + setGeometry(m_geometry); activateMaterial(MatSolidColor); #ifdef QSG_RUNTIME_DESCRIPTION qsgnode_set_description(this, QLatin1String("stroke-fill")); #endif } +QQuickPathItemGenericStrokeFillNode::~QQuickPathItemGenericStrokeFillNode() +{ + delete m_geometry; +} + void QQuickPathItemGenericStrokeFillNode::activateMaterial(Material m) { switch (m) { @@ -104,6 +115,39 @@ void QQuickPathItemGenericStrokeFillNode::activateMaterial(Material m) setMaterial(m_material); } +static bool q_supportsElementIndexUint(QSGRendererInterface::GraphicsApi api) +{ + static bool elementIndexUint = true; +#ifndef QT_NO_OPENGL + if (api == QSGRendererInterface::OpenGL) { + static bool elementIndexUintChecked = false; + if (!elementIndexUintChecked) { + elementIndexUintChecked = true; + QOpenGLContext *context = QOpenGLContext::currentContext(); + QScopedPointer dummyContext; + QScopedPointer dummySurface; + bool ok = true; + if (!context) { + dummyContext.reset(new QOpenGLContext); + dummyContext->create(); + context = dummyContext.data(); + dummySurface.reset(new QOffscreenSurface); + dummySurface->setFormat(context->format()); + dummySurface->create(); + ok = context->makeCurrent(dummySurface.data()); + } + if (ok) { + elementIndexUint = static_cast(context->functions())->hasOpenGLExtension( + QOpenGLExtensions::ElementIndexUint); + } + } + } +#else + Q_UNUSED(api); +#endif + return elementIndexUint; +} + QQuickPathItemGenericRenderer::~QQuickPathItemGenericRenderer() { for (VisualPathData &d : m_vp) { @@ -210,7 +254,7 @@ void QQuickPathItemGenericRenderer::setFillGradient(int index, QQuickPathGradien void QQuickPathItemFillRunnable::run() { - QQuickPathItemGenericRenderer::triangulateFill(path, fillColor, &fillVertices, &fillIndices); + QQuickPathItemGenericRenderer::triangulateFill(path, fillColor, &fillVertices, &fillIndices, &indexType, supportsElementIndexUint); emit done(this); } @@ -262,6 +306,8 @@ void QQuickPathItemGenericRenderer::endSync(bool async) if ((d.syncDirty & DirtyFillGeom) && d.fillColor.a) { d.path.setFillRule(d.fillRule); + if (m_api == QSGRendererInterface::Unknown) + m_api = m_item->window()->rendererInterface()->graphicsApi(); if (async) { QQuickPathItemFillRunnable *r = new QQuickPathItemFillRunnable; r->setAutoDelete(false); @@ -270,6 +316,7 @@ void QQuickPathItemGenericRenderer::endSync(bool async) d.pendingFill = r; r->path = d.path; r->fillColor = d.fillColor; + r->supportsElementIndexUint = q_supportsElementIndexUint(m_api); // Unlikely in practice but in theory m_vp could be // resized. Therefore, capture 'i' instead of 'd'. QObject::connect(r, &QQuickPathItemFillRunnable::done, qApp, [this, i](QQuickPathItemFillRunnable *r) { @@ -279,6 +326,7 @@ void QQuickPathItemGenericRenderer::endSync(bool async) VisualPathData &d(m_vp[i]); d.fillVertices = r->fillVertices; d.fillIndices = r->fillIndices; + d.indexType = r->indexType; d.pendingFill = nullptr; d.effectiveDirty |= DirtyFillGeom; maybeUpdateAsyncItem(); @@ -288,7 +336,7 @@ void QQuickPathItemGenericRenderer::endSync(bool async) didKickOffAsync = true; threadPool.start(r); } else { - triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices); + triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices, &d.indexType, q_supportsElementIndexUint(m_api)); } } @@ -342,12 +390,14 @@ void QQuickPathItemGenericRenderer::maybeUpdateAsyncItem() // thread or some worker thread and must thus be self-contained. void QQuickPathItemGenericRenderer::triangulateFill(const QPainterPath &path, const Color4ub &fillColor, - VerticesType *fillVertices, - IndicesType *fillIndices) + VertexContainerType *fillVertices, + IndexContainerType *fillIndices, + QSGGeometry::Type *indexType, + bool supportsElementIndexUint) { const QVectorPath &vp = qtVectorPathForPath(path); - QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(SCALE, SCALE)); + QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(SCALE, SCALE), 1, supportsElementIndexUint); const int vertexCount = ts.vertices.count() / 2; // just a qreal vector with x,y hence the / 2 fillVertices->resize(vertexCount); ColoredVertex *vdst = reinterpret_cast(fillVertices->data()); @@ -355,21 +405,25 @@ void QQuickPathItemGenericRenderer::triangulateFill(const QPainterPath &path, for (int i = 0; i < vertexCount; ++i) vdst[i].set(vsrc[i * 2] / SCALE, vsrc[i * 2 + 1] / SCALE, fillColor); - fillIndices->resize(ts.indices.size()); - quint16 *idst = fillIndices->data(); + size_t indexByteSize; if (ts.indices.type() == QVertexIndexVector::UnsignedShort) { - memcpy(idst, ts.indices.data(), fillIndices->count() * sizeof(quint16)); + *indexType = QSGGeometry::UnsignedShortType; + // fillIndices is still QVector. Just resize to N/2 and pack + // the N quint16s into it. + fillIndices->resize(ts.indices.size() / 2); + indexByteSize = ts.indices.size() * sizeof(quint16); } else { - const quint32 *isrc = (const quint32 *) ts.indices.data(); - for (int i = 0; i < fillIndices->count(); ++i) - idst[i] = isrc[i]; + *indexType = QSGGeometry::UnsignedIntType; + fillIndices->resize(ts.indices.size()); + indexByteSize = ts.indices.size() * sizeof(quint32); } + memcpy(fillIndices->data(), ts.indices.data(), indexByteSize); } void QQuickPathItemGenericRenderer::triangulateStroke(const QPainterPath &path, const QPen &pen, const Color4ub &strokeColor, - VerticesType *strokeVertices, + VertexContainerType *strokeVertices, const QSize &clipSize) { const QVectorPath &vp = qtVectorPathForPath(path); @@ -504,7 +558,7 @@ void QQuickPathItemGenericRenderer::updateFillNode(VisualPathData *d, QQuickPath QQuickPathItemGenericStrokeFillNode *n = node->m_fillNode; updateShadowDataInNode(d, n); - QSGGeometry *g = &n->m_geometry; + QSGGeometry *g = n->m_geometry; if (d->fillVertices.isEmpty()) { if (g->vertexCount() || g->indexCount()) { g->allocate(0, 0); @@ -534,7 +588,17 @@ void QQuickPathItemGenericRenderer::updateFillNode(VisualPathData *d, QQuickPath } } - g->allocate(d->fillVertices.count(), d->fillIndices.count()); + const int indexCount = d->indexType == QSGGeometry::UnsignedShortType + ? d->fillIndices.count() * 2 : d->fillIndices.count(); + if (g->indexType() != d->indexType) { + g = new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), + d->fillVertices.count(), indexCount, d->indexType); + n->setGeometry(g); + delete n->m_geometry; + n->m_geometry = g; + } else { + g->allocate(d->fillVertices.count(), indexCount); + } g->setDrawingMode(QSGGeometry::DrawTriangles); memcpy(g->vertexData(), d->fillVertices.constData(), g->vertexCount() * g->sizeOfVertex()); memcpy(g->indexData(), d->fillIndices.constData(), g->indexCount() * g->sizeOfIndex()); @@ -550,7 +614,7 @@ void QQuickPathItemGenericRenderer::updateStrokeNode(VisualPathData *d, QQuickPa return; QQuickPathItemGenericStrokeFillNode *n = node->m_strokeNode; - QSGGeometry *g = &n->m_geometry; + QSGGeometry *g = n->m_geometry; if (d->strokeVertices.isEmpty()) { if (g->vertexCount() || g->indexCount()) { g->allocate(0, 0); diff --git a/src/quick/items/qquickpathitemgenericrenderer_p.h b/src/quick/items/qquickpathitemgenericrenderer_p.h index ca05492841..045c52d610 100644 --- a/src/quick/items/qquickpathitemgenericrenderer_p.h +++ b/src/quick/items/qquickpathitemgenericrenderer_p.h @@ -55,6 +55,7 @@ #include #include #include +#include #include QT_BEGIN_NAMESPACE @@ -77,6 +78,7 @@ public: QQuickPathItemGenericRenderer(QQuickItem *item) : m_item(item), + m_api(QSGRendererInterface::Unknown), m_rootNode(nullptr), m_accDirty(0), m_asyncCallback(nullptr) @@ -103,17 +105,19 @@ public: void setRootNode(QQuickPathItemGenericNode *node); struct Color4ub { unsigned char r, g, b, a; }; - typedef QVector VerticesType; - typedef QVector IndicesType; + typedef QVector VertexContainerType; + typedef QVector IndexContainerType; static void triangulateFill(const QPainterPath &path, const Color4ub &fillColor, - VerticesType *fillVertices, - IndicesType *fillIndices); + VertexContainerType *fillVertices, + IndexContainerType *fillIndices, + QSGGeometry::Type *indexType, + bool supportsElementIndexUint); static void triangulateStroke(const QPainterPath &path, const QPen &pen, const Color4ub &strokeColor, - VerticesType *strokeVertices, + VertexContainerType *strokeVertices, const QSize &clipSize); private: @@ -128,9 +132,10 @@ private: QPainterPath path; bool fillGradientActive; QQuickPathItemGradientCache::GradientDesc fillGradient; - VerticesType fillVertices; - IndicesType fillIndices; - VerticesType strokeVertices; + VertexContainerType fillVertices; + IndexContainerType fillIndices; + QSGGeometry::Type indexType; + VertexContainerType strokeVertices; int syncDirty; int effectiveDirty = 0; QQuickPathItemFillRunnable *pendingFill = nullptr; @@ -142,6 +147,7 @@ private: void updateStrokeNode(VisualPathData *d, QQuickPathItemGenericNode *node); QQuickItem *m_item; + QSGRendererInterface::GraphicsApi m_api; QQuickPathItemGenericNode *m_rootNode; QVector m_vp; int m_accDirty; @@ -157,10 +163,16 @@ public: void run() override; bool orphaned = false; + + // input QPainterPath path; QQuickPathItemGenericRenderer::Color4ub fillColor; - QQuickPathItemGenericRenderer::VerticesType fillVertices; - QQuickPathItemGenericRenderer::IndicesType fillIndices; + bool supportsElementIndexUint; + + // output + QQuickPathItemGenericRenderer::VertexContainerType fillVertices; + QQuickPathItemGenericRenderer::IndexContainerType fillIndices; + QSGGeometry::Type indexType; Q_SIGNALS: void done(QQuickPathItemFillRunnable *self); @@ -174,12 +186,16 @@ public: void run() override; bool orphaned = false; + + // input QPainterPath path; QPen pen; QQuickPathItemGenericRenderer::Color4ub strokeColor; - QQuickPathItemGenericRenderer::VerticesType strokeVertices; QSize clipSize; + // output + QQuickPathItemGenericRenderer::VertexContainerType strokeVertices; + Q_SIGNALS: void done(QQuickPathItemStrokeRunnable *self); }; @@ -188,6 +204,7 @@ class QQuickPathItemGenericStrokeFillNode : public QSGGeometryNode { public: QQuickPathItemGenericStrokeFillNode(QQuickWindow *window); + ~QQuickPathItemGenericStrokeFillNode(); enum Material { MatSolidColor, @@ -202,7 +219,7 @@ public: QQuickPathItemGradientCache::GradientDesc m_fillGradient; private: - QSGGeometry m_geometry; + QSGGeometry *m_geometry; QQuickWindow *m_window; QSGMaterial *m_material; QScopedPointer m_solidColorMaterial; -- cgit v1.2.3 From 5daaec1e193bc69f55d4ddbfef8911ce9810ea28 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 11 Jan 2017 17:05:14 +0100 Subject: Improve stencil clipping with NVPR So that it actually performs when clipping the tiger. Add an example. Change-Id: I7c1c6244710febdb6b02852ebca094665adec417 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitem.cpp | 2 +- src/quick/items/qquickpathitemnvprrenderer.cpp | 54 ++++++++++++++------------ src/quick/items/qquickpathitemnvprrenderer_p.h | 9 +++-- 3 files changed, 36 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp index 306e79dc1e..b07a7a6c06 100644 --- a/src/quick/items/qquickpathitem.cpp +++ b/src/quick/items/qquickpathitem.cpp @@ -587,7 +587,7 @@ QSGNode *QQuickPathItemPrivate::createNode() #ifndef QT_NO_OPENGL case QSGRendererInterface::OpenGL: if (QQuickPathItemNvprRenderNode::isSupported()) { - node = new QQuickPathItemNvprRenderNode(q); + node = new QQuickPathItemNvprRenderNode; static_cast(renderer)->setNode( static_cast(node)); } else { diff --git a/src/quick/items/qquickpathitemnvprrenderer.cpp b/src/quick/items/qquickpathitemnvprrenderer.cpp index 9303f698ac..13fab2dc76 100644 --- a/src/quick/items/qquickpathitemnvprrenderer.cpp +++ b/src/quick/items/qquickpathitemnvprrenderer.cpp @@ -397,11 +397,6 @@ bool QQuickPathItemNvprRenderNode::nvprInited = false; QQuickNvprFunctions QQuickPathItemNvprRenderNode::nvpr; QQuickNvprMaterialManager QQuickPathItemNvprRenderNode::mtlmgr; -QQuickPathItemNvprRenderNode::QQuickPathItemNvprRenderNode(QQuickPathItem *item) - : m_item(item) -{ -} - QQuickPathItemNvprRenderNode::~QQuickPathItemNvprRenderNode() { releaseResources(); @@ -528,6 +523,9 @@ void QQuickPathItemNvprRenderNode::updatePath(VisualPathRenderData *d) // count == 0 -> no dash nvpr.pathDashArray(d->path, d->dashPattern.count(), d->dashPattern.constData()); } + + if (d->dirty) + d->fallbackValid = false; } void QQuickPathItemNvprRenderNode::renderStroke(VisualPathRenderData *d, int strokeStencilValue, int writeMask) @@ -568,23 +566,28 @@ void QQuickPathItemNvprRenderNode::renderFill(VisualPathRenderData *d) void QQuickPathItemNvprRenderNode::renderOffscreenFill(VisualPathRenderData *d) { - QQuickWindow *w = m_item->window(); - const qreal dpr = w->effectiveDevicePixelRatio(); - QSize itemSize = QSize(m_item->width(), m_item->height()) * dpr; - QSize rtSize = w->renderTargetSize(); - if (rtSize.isEmpty()) - rtSize = w->size() * dpr; + if (d->fallbackValid && d->fallbackFbo) + return; + + GLfloat bb[4]; + nvpr.getPathParameterfv(d->path, GL_PATH_STROKE_BOUNDING_BOX_NV, bb); + QSize sz = QSizeF(bb[2] - bb[0] + 1, bb[3] - bb[1] + 1).toSize(); + d->fallbackSize = QSize(qMax(32, sz.width()), qMax(32, sz.height())); + d->fallbackTopLeft = QPointF(bb[0], bb[1]); - if (d->fallbackFbo && d->fallbackFbo->size() != itemSize) { + if (d->fallbackFbo && d->fallbackFbo->size() != d->fallbackSize) { delete d->fallbackFbo; d->fallbackFbo = nullptr; } if (!d->fallbackFbo) - d->fallbackFbo = new QOpenGLFramebufferObject(itemSize, QOpenGLFramebufferObject::CombinedDepthStencil); + d->fallbackFbo = new QOpenGLFramebufferObject(d->fallbackSize, QOpenGLFramebufferObject::CombinedDepthStencil); if (!d->fallbackFbo->bind()) return; - f->glViewport(0, 0, itemSize.width(), itemSize.height()); + GLint prevViewport[4]; + f->glGetIntegerv(GL_VIEWPORT, prevViewport); + + f->glViewport(0, 0, d->fallbackSize.width(), d->fallbackSize.height()); f->glDisable(GL_DEPTH_TEST); f->glClearColor(0, 0, 0, 0); f->glClearStencil(0); @@ -592,16 +595,20 @@ void QQuickPathItemNvprRenderNode::renderOffscreenFill(VisualPathRenderData *d) f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - nvpr.matrixLoadIdentity(GL_PATH_MODELVIEW_NV); + QMatrix4x4 mv; + mv.translate(-d->fallbackTopLeft.x(), -d->fallbackTopLeft.y()); + nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, mv.constData()); QMatrix4x4 proj; - proj.ortho(0, itemSize.width(), itemSize.height(), 0, 1, -1); + proj.ortho(0, d->fallbackSize.width(), d->fallbackSize.height(), 0, 1, -1); nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, proj.constData()); renderFill(d); d->fallbackFbo->release(); f->glEnable(GL_DEPTH_TEST); - f->glViewport(0, 0, rtSize.width(), rtSize.height()); + f->glViewport(prevViewport[0], prevViewport[1], prevViewport[2], prevViewport[3]); + + d->fallbackValid = true; } void QQuickPathItemNvprRenderNode::setupStencilForCover(bool stencilClip, int sv) @@ -655,8 +662,8 @@ void QQuickPathItemNvprRenderNode::render(const RenderState *state) for (VisualPathRenderData &d : m_vp) { updatePath(&d); - const bool hasFill = !qFuzzyIsNull(d.fillColor.w()) || d.fillGradientActive; - const bool hasStroke = d.strokeWidth >= 0.0f && !qFuzzyIsNull(d.strokeColor.w()); + const bool hasFill = d.hasFill(); + const bool hasStroke = d.hasStroke(); if (hasFill && stencilClip) { // Fall back to a texture when complex clipping is in use and we have @@ -686,8 +693,10 @@ void QQuickPathItemNvprRenderNode::render(const RenderState *state) m_fallbackBlitter.create(); f->glStencilFunc(GL_EQUAL, sv, 0xFF); f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + QMatrix4x4 mv = *matrix(); + mv.translate(d.fallbackTopLeft.x(), d.fallbackTopLeft.y()); m_fallbackBlitter.texturedQuad(d.fallbackFbo->texture(), d.fallbackFbo->size(), - *state->projectionMatrix(), *matrix(), + *state->projectionMatrix(), mv, inheritedOpacity()); } } @@ -729,11 +738,6 @@ QSGRenderNode::RenderingFlags QQuickPathItemNvprRenderNode::flags() const return DepthAwareRendering; // avoid hitting the less optimal no-opaque-batch path in the renderer } -QRectF QQuickPathItemNvprRenderNode::rect() const -{ - return QRect(0, 0, m_item->width(), m_item->height()); -} - bool QQuickPathItemNvprRenderNode::isSupported() { static const bool nvprDisabled = qEnvironmentVariableIntValue("QT_NO_NVPR") != 0; diff --git a/src/quick/items/qquickpathitemnvprrenderer_p.h b/src/quick/items/qquickpathitemnvprrenderer_p.h index 1617de17e6..61f8b5ebb9 100644 --- a/src/quick/items/qquickpathitemnvprrenderer_p.h +++ b/src/quick/items/qquickpathitemnvprrenderer_p.h @@ -176,14 +176,12 @@ private: class QQuickPathItemNvprRenderNode : public QSGRenderNode { public: - QQuickPathItemNvprRenderNode(QQuickPathItem *item); ~QQuickPathItemNvprRenderNode(); void render(const RenderState *state) override; void releaseResources() override; StateFlags changedStates() const override; RenderingFlags flags() const override; - QRectF rect() const override; static bool isSupported(); @@ -204,6 +202,12 @@ private: bool fillGradientActive; QQuickPathItemGradientCache::GradientDesc fillGradient; QOpenGLFramebufferObject *fallbackFbo = nullptr; + bool fallbackValid = false; + QSize fallbackSize; + QPointF fallbackTopLeft; + + bool hasFill() const { return !qFuzzyIsNull(fillColor.w()) || fillGradientActive; } + bool hasStroke() const { return strokeWidth >= 0.0f && !qFuzzyIsNull(strokeColor.w()); } }; void updatePath(VisualPathRenderData *d); @@ -216,7 +220,6 @@ private: static QQuickNvprFunctions nvpr; static QQuickNvprMaterialManager mtlmgr; - QQuickPathItem *m_item; QQuickNvprBlitter m_fallbackBlitter; QOpenGLExtraFunctions *f = nullptr; -- cgit v1.2.3 From 364de6e01c88cef1ec2f1a6d13dc8d356e159ad0 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 16 Jan 2017 11:26:52 +0100 Subject: PathItem API cleanup: rename visualPaths to elements visualPaths looks somewhat odd. Not that it matters much since it is the default property anyway, but pick the commonly used "elements" instead. This also allows easier renaming of VisualPath to something better in the future, in case a suitable candidate comes up. Change-Id: I0cdc18cdec49ff821a75f45073598b31f5de9bf8 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitem.cpp | 2 +- src/quick/items/qquickpathitem_p.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp index b07a7a6c06..c5ad73e960 100644 --- a/src/quick/items/qquickpathitem.cpp +++ b/src/quick/items/qquickpathitem.cpp @@ -465,7 +465,7 @@ static void vpe_clear(QQmlListProperty *property) d->_q_visualPathChanged(); } -QQmlListProperty QQuickPathItem::visualPaths() +QQmlListProperty QQuickPathItem::elements() { return QQmlListProperty(this, nullptr, diff --git a/src/quick/items/qquickpathitem_p.h b/src/quick/items/qquickpathitem_p.h index 1b36348cd2..9ca1418d9e 100644 --- a/src/quick/items/qquickpathitem_p.h +++ b/src/quick/items/qquickpathitem_p.h @@ -265,8 +265,8 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPathItem : public QQuickItem Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged) Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) Q_PROPERTY(Status status READ status NOTIFY statusChanged) - Q_PROPERTY(QQmlListProperty visualPaths READ visualPaths) - Q_CLASSINFO("DefaultProperty", "visualPaths") + Q_PROPERTY(QQmlListProperty elements READ elements) + Q_CLASSINFO("DefaultProperty", "elements") public: enum RendererType { @@ -294,7 +294,7 @@ public: Status status() const; - QQmlListProperty visualPaths(); + QQmlListProperty elements(); protected: QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override; -- cgit v1.2.3 From 5c2ba6db53b2190160081695faff521bc367e33d Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Sun, 22 Jan 2017 17:25:17 +0100 Subject: Destroy the async PathItem work pool properly Avoid getting warnings from QWaitCondition on Windows by managing the lifetime of the QThreadPool ourselves. Change-Id: Idc3545a2336f7971bd382560aa958a325ac69fc6 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemgenericrenderer.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp index bf9506ba65..3de01a5bc7 100644 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -270,6 +270,14 @@ void QQuickPathItemGenericRenderer::setAsyncCallback(void (*callback)(void *), v m_asyncCallbackData = data; } +static QThreadPool *pathWorkThreadPool = nullptr; + +static void deletePathWorkThreadPool() +{ + delete pathWorkThreadPool; + pathWorkThreadPool = nullptr; +} + void QQuickPathItemGenericRenderer::endSync(bool async) { bool didKickOffAsync = false; @@ -295,13 +303,11 @@ void QQuickPathItemGenericRenderer::endSync(bool async) continue; } - static QThreadPool threadPool; - static bool threadPoolReady = false; - - if (async && !threadPoolReady) { - threadPoolReady = true; + if (async && !pathWorkThreadPool) { + qAddPostRoutine(deletePathWorkThreadPool); + pathWorkThreadPool = new QThreadPool; const int idealCount = QThread::idealThreadCount(); - threadPool.setMaxThreadCount(idealCount > 0 ? idealCount * 2 : 4); + pathWorkThreadPool->setMaxThreadCount(idealCount > 0 ? idealCount * 2 : 4); } if ((d.syncDirty & DirtyFillGeom) && d.fillColor.a) { @@ -334,7 +340,7 @@ void QQuickPathItemGenericRenderer::endSync(bool async) r->deleteLater(); }); didKickOffAsync = true; - threadPool.start(r); + pathWorkThreadPool->start(r); } else { triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices, &d.indexType, q_supportsElementIndexUint(m_api)); } @@ -362,7 +368,7 @@ void QQuickPathItemGenericRenderer::endSync(bool async) r->deleteLater(); }); didKickOffAsync = true; - threadPool.start(r); + pathWorkThreadPool->start(r); } else { triangulateStroke(d.path, d.pen, d.strokeColor, &d.strokeVertices, QSize(m_item->width(), m_item->height())); -- cgit v1.2.3 From a48244d5aa1d14c286b7cd39afebcfff9c9dcb60 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 24 Jan 2017 18:27:27 +0100 Subject: JS API for defining static paths and draw params In order to avoid the over head of one QObject for each VisualPath, Path and PathXxxx element, provide an optional JavaScript API. PathItem { VisualPath { strokeWidth: 4; fillColor: "blue"; dashPattern: [ 1, 2, 3, 4]; capStyle: RoundCap path: Path { PathMove { x: 100; y: 200 } PathLine { x: 300; y: 300 } } } can now also be written as, at least when the path and the stroke/fill params are static and do not need changing/animating later on: PathItem { Component.onCompleted: { var path = newPath(); var sfp = newStrokeFillParams(); path.moveTo(100, 200); path.lineTo(300, 300); sfp.strokeWidth = 4; sfp.fillColor = "blue"; sfp.dashPattern = [ 1, 2, 3, 4 ]; sfp.capStyle = VisualPath.RoundCap; appendVisualPath(path, sfp); commitVisualPaths(); } } In order to emphasize the difference from an imperative API (like context2d), keep the path and the path stroke/fill parameters separate. To preserve our sanity, extras like gradients are not mapped to JavaScript, instead, one still references an QML-defined object from properties like fillGradient. The objects from newPath() and newStrokeFillParams() are reusable by calling clear(). This avoids the need for multiple temp objects when there are multiple paths. Add a simple test and a hidden stress test with the tiger to the manual test. Change-Id: I3b1e275bacf8c8fc52f585fbed5d6f9354d5ae8e Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitem.cpp | 814 +++++++++++++++++++-- src/quick/items/qquickpathitem_p.h | 7 + src/quick/items/qquickpathitem_p_p.h | 103 ++- src/quick/items/qquickpathitemgenericrenderer.cpp | 7 + src/quick/items/qquickpathitemgenericrenderer_p.h | 1 + src/quick/items/qquickpathitemnvprrenderer.cpp | 84 +++ src/quick/items/qquickpathitemnvprrenderer_p.h | 2 + src/quick/items/qquickpathitemsoftwarerenderer.cpp | 8 + src/quick/items/qquickpathitemsoftwarerenderer_p.h | 1 + 9 files changed, 938 insertions(+), 89 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp index c5ad73e960..61216be9b0 100644 --- a/src/quick/items/qquickpathitem.cpp +++ b/src/quick/items/qquickpathitem.cpp @@ -43,15 +43,20 @@ #include "qquickpathitemnvprrenderer_p.h" #include "qquickpathitemsoftwarerenderer_p.h" #include +#include #include #include +#include +#include +#include +#include +#include + QT_BEGIN_NAMESPACE -QQuickVisualPathPrivate::QQuickVisualPathPrivate() - : path(nullptr), - dirty(DirtyAll), - strokeColor(Qt::white), +QQuickPathItemStrokeFillParams::QQuickPathItemStrokeFillParams() + : strokeColor(Qt::white), strokeWidth(1), fillColor(Qt::white), fillRule(QQuickVisualPath::OddEvenFill), @@ -65,6 +70,56 @@ QQuickVisualPathPrivate::QQuickVisualPathPrivate() dashPattern << 4 << 2; // 4 * strokeWidth dash followed by 2 * strokeWidth space } +QPainterPath QQuickPathItemPath::toPainterPath() const +{ + QPainterPath p; + int coordIdx = 0; + for (int i = 0; i < cmd.count(); ++i) { + switch (cmd[i]) { + case QQuickPathItemPath::MoveTo: + p.moveTo(coords[coordIdx], coords[coordIdx + 1]); + coordIdx += 2; + break; + case QQuickPathItemPath::LineTo: + p.lineTo(coords[coordIdx], coords[coordIdx + 1]); + coordIdx += 2; + break; + case QQuickPathItemPath::QuadTo: + p.quadTo(coords[coordIdx], coords[coordIdx + 1], + coords[coordIdx + 2], coords[coordIdx + 3]); + coordIdx += 4; + break; + case QQuickPathItemPath::CubicTo: + p.cubicTo(coords[coordIdx], coords[coordIdx + 1], + coords[coordIdx + 2], coords[coordIdx + 3], + coords[coordIdx + 4], coords[coordIdx + 5]); + coordIdx += 6; + break; + case QQuickPathItemPath::ArcTo: + // does not map to the QPainterPath API; reuse the helper code from QQuickSvgParser + QQuickSvgParser::pathArc(p, + coords[coordIdx], coords[coordIdx + 1], // radius + coords[coordIdx + 2], // xAxisRotation + !qFuzzyIsNull(coords[coordIdx + 6]), // useLargeArc + !qFuzzyIsNull(coords[coordIdx + 5]), // sweep flag + coords[coordIdx + 3], coords[coordIdx + 4], // end + p.currentPosition().x(), p.currentPosition().y()); + coordIdx += 7; + break; + default: + qWarning("Unknown JS path command: %d", cmd[i]); + break; + } + } + return p; +} + +QQuickVisualPathPrivate::QQuickVisualPathPrivate() + : path(nullptr), + dirty(DirtyAll) +{ +} + QQuickVisualPath::QQuickVisualPath(QObject *parent) : QObject(*(new QQuickVisualPathPrivate), parent) { @@ -108,14 +163,14 @@ void QQuickVisualPathPrivate::_q_pathChanged() QColor QQuickVisualPath::strokeColor() const { Q_D(const QQuickVisualPath); - return d->strokeColor; + return d->sfp.strokeColor; } void QQuickVisualPath::setStrokeColor(const QColor &color) { Q_D(QQuickVisualPath); - if (d->strokeColor != color) { - d->strokeColor = color; + if (d->sfp.strokeColor != color) { + d->sfp.strokeColor = color; d->dirty |= QQuickVisualPathPrivate::DirtyStrokeColor; emit strokeColorChanged(); emit changed(); @@ -125,14 +180,14 @@ void QQuickVisualPath::setStrokeColor(const QColor &color) qreal QQuickVisualPath::strokeWidth() const { Q_D(const QQuickVisualPath); - return d->strokeWidth; + return d->sfp.strokeWidth; } void QQuickVisualPath::setStrokeWidth(qreal w) { Q_D(QQuickVisualPath); - if (d->strokeWidth != w) { - d->strokeWidth = w; + if (d->sfp.strokeWidth != w) { + d->sfp.strokeWidth = w; d->dirty |= QQuickVisualPathPrivate::DirtyStrokeWidth; emit strokeWidthChanged(); emit changed(); @@ -142,14 +197,14 @@ void QQuickVisualPath::setStrokeWidth(qreal w) QColor QQuickVisualPath::fillColor() const { Q_D(const QQuickVisualPath); - return d->fillColor; + return d->sfp.fillColor; } void QQuickVisualPath::setFillColor(const QColor &color) { Q_D(QQuickVisualPath); - if (d->fillColor != color) { - d->fillColor = color; + if (d->sfp.fillColor != color) { + d->sfp.fillColor = color; d->dirty |= QQuickVisualPathPrivate::DirtyFillColor; emit fillColorChanged(); emit changed(); @@ -159,14 +214,14 @@ void QQuickVisualPath::setFillColor(const QColor &color) QQuickVisualPath::FillRule QQuickVisualPath::fillRule() const { Q_D(const QQuickVisualPath); - return d->fillRule; + return d->sfp.fillRule; } void QQuickVisualPath::setFillRule(FillRule fillRule) { Q_D(QQuickVisualPath); - if (d->fillRule != fillRule) { - d->fillRule = fillRule; + if (d->sfp.fillRule != fillRule) { + d->sfp.fillRule = fillRule; d->dirty |= QQuickVisualPathPrivate::DirtyFillRule; emit fillRuleChanged(); emit changed(); @@ -176,14 +231,14 @@ void QQuickVisualPath::setFillRule(FillRule fillRule) QQuickVisualPath::JoinStyle QQuickVisualPath::joinStyle() const { Q_D(const QQuickVisualPath); - return d->joinStyle; + return d->sfp.joinStyle; } void QQuickVisualPath::setJoinStyle(JoinStyle style) { Q_D(QQuickVisualPath); - if (d->joinStyle != style) { - d->joinStyle = style; + if (d->sfp.joinStyle != style) { + d->sfp.joinStyle = style; d->dirty |= QQuickVisualPathPrivate::DirtyStyle; emit joinStyleChanged(); emit changed(); @@ -193,14 +248,14 @@ void QQuickVisualPath::setJoinStyle(JoinStyle style) int QQuickVisualPath::miterLimit() const { Q_D(const QQuickVisualPath); - return d->miterLimit; + return d->sfp.miterLimit; } void QQuickVisualPath::setMiterLimit(int limit) { Q_D(QQuickVisualPath); - if (d->miterLimit != limit) { - d->miterLimit = limit; + if (d->sfp.miterLimit != limit) { + d->sfp.miterLimit = limit; d->dirty |= QQuickVisualPathPrivate::DirtyStyle; emit miterLimitChanged(); emit changed(); @@ -210,14 +265,14 @@ void QQuickVisualPath::setMiterLimit(int limit) QQuickVisualPath::CapStyle QQuickVisualPath::capStyle() const { Q_D(const QQuickVisualPath); - return d->capStyle; + return d->sfp.capStyle; } void QQuickVisualPath::setCapStyle(CapStyle style) { Q_D(QQuickVisualPath); - if (d->capStyle != style) { - d->capStyle = style; + if (d->sfp.capStyle != style) { + d->sfp.capStyle = style; d->dirty |= QQuickVisualPathPrivate::DirtyStyle; emit capStyleChanged(); emit changed(); @@ -227,14 +282,14 @@ void QQuickVisualPath::setCapStyle(CapStyle style) QQuickVisualPath::StrokeStyle QQuickVisualPath::strokeStyle() const { Q_D(const QQuickVisualPath); - return d->strokeStyle; + return d->sfp.strokeStyle; } void QQuickVisualPath::setStrokeStyle(StrokeStyle style) { Q_D(QQuickVisualPath); - if (d->strokeStyle != style) { - d->strokeStyle = style; + if (d->sfp.strokeStyle != style) { + d->sfp.strokeStyle = style; d->dirty |= QQuickVisualPathPrivate::DirtyDash; emit strokeStyleChanged(); emit changed(); @@ -244,14 +299,14 @@ void QQuickVisualPath::setStrokeStyle(StrokeStyle style) qreal QQuickVisualPath::dashOffset() const { Q_D(const QQuickVisualPath); - return d->dashOffset; + return d->sfp.dashOffset; } void QQuickVisualPath::setDashOffset(qreal offset) { Q_D(QQuickVisualPath); - if (d->dashOffset != offset) { - d->dashOffset = offset; + if (d->sfp.dashOffset != offset) { + d->sfp.dashOffset = offset; d->dirty |= QQuickVisualPathPrivate::DirtyDash; emit dashOffsetChanged(); emit changed(); @@ -261,14 +316,14 @@ void QQuickVisualPath::setDashOffset(qreal offset) QVector QQuickVisualPath::dashPattern() const { Q_D(const QQuickVisualPath); - return d->dashPattern; + return d->sfp.dashPattern; } void QQuickVisualPath::setDashPattern(const QVector &array) { Q_D(QQuickVisualPath); - if (d->dashPattern != array) { - d->dashPattern = array; + if (d->sfp.dashPattern != array) { + d->sfp.dashPattern = array; d->dirty |= QQuickVisualPathPrivate::DirtyDash; emit dashPatternChanged(); emit changed(); @@ -278,19 +333,19 @@ void QQuickVisualPath::setDashPattern(const QVector &array) QQuickPathGradient *QQuickVisualPath::fillGradient() const { Q_D(const QQuickVisualPath); - return d->fillGradient; + return d->sfp.fillGradient; } void QQuickVisualPath::setFillGradient(QQuickPathGradient *gradient) { Q_D(QQuickVisualPath); - if (d->fillGradient != gradient) { - if (d->fillGradient) - qmlobject_disconnect(d->fillGradient, QQuickPathGradient, SIGNAL(updated()), + if (d->sfp.fillGradient != gradient) { + if (d->sfp.fillGradient) + qmlobject_disconnect(d->sfp.fillGradient, QQuickPathGradient, SIGNAL(updated()), this, QQuickVisualPath, SLOT(_q_fillGradientChanged())); - d->fillGradient = gradient; - if (d->fillGradient) - qmlobject_connect(d->fillGradient, QQuickPathGradient, SIGNAL(updated()), + d->sfp.fillGradient = gradient; + if (d->sfp.fillGradient) + qmlobject_connect(d->sfp.fillGradient, QQuickPathGradient, SIGNAL(updated()), this, QQuickVisualPath, SLOT(_q_fillGradientChanged())); d->dirty |= QQuickVisualPathPrivate::DirtyFillGradient; emit changed(); @@ -430,14 +485,14 @@ QQuickPathItem::Status QQuickPathItem::status() const static QQuickVisualPath *vpe_at(QQmlListProperty *property, int index) { QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast(property->object)); - return d->vp.at(index); + return d->qmlData.vp.at(index); } static void vpe_append(QQmlListProperty *property, QQuickVisualPath *obj) { QQuickPathItem *item = static_cast(property->object); QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(item); - d->vp.append(obj); + d->qmlData.vp.append(obj); if (d->componentComplete) { QObject::connect(obj, SIGNAL(changed()), item, SLOT(_q_visualPathChanged())); @@ -448,7 +503,7 @@ static void vpe_append(QQmlListProperty *property, QQuickVisua static int vpe_count(QQmlListProperty *property) { QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast(property->object)); - return d->vp.count(); + return d->qmlData.vp.count(); } static void vpe_clear(QQmlListProperty *property) @@ -456,10 +511,10 @@ static void vpe_clear(QQmlListProperty *property) QQuickPathItem *item = static_cast(property->object); QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(item); - for (QQuickVisualPath *p : d->vp) + for (QQuickVisualPath *p : d->qmlData.vp) QObject::disconnect(p, SIGNAL(changed()), item, SLOT(_q_visualPathChanged())); - d->vp.clear(); + d->qmlData.vp.clear(); if (d->componentComplete) d->_q_visualPathChanged(); @@ -486,7 +541,7 @@ void QQuickPathItem::componentComplete() Q_D(QQuickPathItem); d->componentComplete = true; - for (QQuickVisualPath *p : d->vp) + for (QQuickVisualPath *p : d->qmlData.vp) connect(p, SIGNAL(changed()), this, SLOT(_q_visualPathChanged())); d->_q_visualPathChanged(); @@ -624,36 +679,60 @@ void QQuickPathItemPrivate::sync() renderer->setAsyncCallback(q_asyncPathItemReady, this); } - 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 (!jsData.isValid()) { + // Standard route: The path and stroke/fill parameters are provided via + // VisualPath and Path. + const int count = qmlData.vp.count(); + renderer->beginSync(count); + + for (int i = 0; i < count; ++i) { + QQuickVisualPath *p = qmlData.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 & QQuickVisualPathPrivate::DirtyDash) - renderer->setStrokeStyle(i, p->strokeStyle(), p->dashOffset(), p->dashPattern()); - if (dirty & QQuickVisualPathPrivate::DirtyFillGradient) - renderer->setFillGradient(i, p->fillGradient()); - dirty = 0; - } + renderer->endSync(useAsync); + } else { + // Path and stroke/fill params provided from JavaScript. This avoids + // QObjects at the expense of not supporting changes afterwards. + const int count = jsData.paths.count(); + renderer->beginSync(count); + + for (int i = 0; i < count; ++i) { + renderer->setJSPath(i, jsData.paths[i]); + const QQuickPathItemStrokeFillParams sfp(jsData.sfp[i]); + renderer->setStrokeColor(i, sfp.strokeColor); + renderer->setStrokeWidth(i, sfp.strokeWidth); + renderer->setFillColor(i, sfp.fillColor); + renderer->setFillRule(i, sfp.fillRule); + renderer->setJoinStyle(i, sfp.joinStyle, sfp.miterLimit); + renderer->setCapStyle(i, sfp.capStyle); + renderer->setStrokeStyle(i, sfp.strokeStyle, sfp.dashOffset, sfp.dashPattern); + renderer->setFillGradient(i, sfp.fillGradient); + } - renderer->endSync(useAsync); + renderer->endSync(useAsync); + } if (!useAsync) setStatus(QQuickPathItem::Ready); @@ -935,6 +1014,593 @@ QSGTexture *QQuickPathItemGradientCache::get(const GradientDesc &grad) #endif // QT_NO_OPENGL +// ***** JS-based alternative for creating static paths, (mostly) without QObjects ***** + +class QQuickPathItemJSEngineData : public QV8Engine::Deletable +{ +public: + QQuickPathItemJSEngineData(QV4::ExecutionEngine *engine); + + QV4::PersistentValue pathProto; + QV4::PersistentValue strokeFillParamsProto; +}; + +V4_DEFINE_EXTENSION(QQuickPathItemJSEngineData, engineData) + +namespace QV4 { +namespace Heap { + +struct QQuickPathItemJSPathPrototype : Object { + void init() { Object::init(); } +}; + +struct QQuickPathItemJSPath : Object { + void init() { Object::init(); } + QQuickPathItemPathObject *obj; +}; + +struct QQuickPathItemJSStrokeFillParamsPrototype : Object { + void init() { Object::init(); } +}; + +struct QQuickPathItemJSStrokeFillParams : Object { + void init() { Object::init(); } + QQuickPathItemStrokeFillParamsObject *obj; +}; + +} // namespace Heap +} // namespace QV4 + +struct QQuickPathItemJSPathPrototype : public QV4::Object +{ + V4_OBJECT2(QQuickPathItemJSPathPrototype, QV4::Object) +public: + static QV4::Heap::QQuickPathItemJSPathPrototype *create(QV4::ExecutionEngine *engine) + { + QV4::Scope scope(engine); + auto obj = engine->memoryManager->allocObject(); + QV4::Scoped o(scope, obj); + + o->defineDefaultProperty(QStringLiteral("clear"), method_clear, 0); + o->defineDefaultProperty(QStringLiteral("moveTo"), method_moveTo, 0); + o->defineDefaultProperty(QStringLiteral("lineTo"), method_lineTo, 0); + o->defineDefaultProperty(QStringLiteral("quadTo"), method_quadTo, 0); + o->defineDefaultProperty(QStringLiteral("cubicTo"), method_cubicTo, 0); + o->defineDefaultProperty(QStringLiteral("arcTo"), method_arcTo, 0); + + return o->d(); + } + + static void method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_moveTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_lineTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_quadTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_cubicTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_arcTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); +}; + +DEFINE_OBJECT_VTABLE(QQuickPathItemJSPathPrototype); + +struct QQuickPathItemJSStrokeFillParamsPrototype : public QV4::Object +{ + V4_OBJECT2(QQuickPathItemJSStrokeFillParamsPrototype, QV4::Object) +public: + static QV4::Heap::QQuickPathItemJSStrokeFillParamsPrototype *create(QV4::ExecutionEngine *engine) + { + QV4::Scope scope(engine); + auto obj = engine->memoryManager->allocObject(); + QV4::Scoped o(scope, obj); + + o->defineDefaultProperty(QStringLiteral("clear"), method_clear, 0); + + return o->d(); + } + + static void method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); +}; + +DEFINE_OBJECT_VTABLE(QQuickPathItemJSStrokeFillParamsPrototype); + +struct QQuickPathItemJSPath : public QV4::Object +{ + V4_OBJECT2(QQuickPathItemJSPath, QV4::Object) +}; + +DEFINE_OBJECT_VTABLE(QQuickPathItemJSPath); + +struct QQuickPathItemJSStrokeFillParams : public QV4::Object +{ + V4_OBJECT2(QQuickPathItemJSStrokeFillParams, QV4::Object) + + static void method_get_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); +}; + +DEFINE_OBJECT_VTABLE(QQuickPathItemJSStrokeFillParams); + +void QQuickPathItemJSPathPrototype::method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + r->d()->obj->clear(); + + scope.result = callData->thisObject.asReturnedValue(); +} + +void QQuickPathItemJSPathPrototype::method_moveTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + if (callData->argc >= 2) { + QQuickPathItemPathObject *p = r->d()->obj; + p->path.cmd.append(QQuickPathItemPath::MoveTo); + p->path.coords.append(callData->args[0].toNumber()); + p->path.coords.append(callData->args[1].toNumber()); + } + + scope.result = callData->thisObject.asReturnedValue(); +} + +void QQuickPathItemJSPathPrototype::method_lineTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + if (callData->argc >= 2) { + QQuickPathItemPathObject *p = r->d()->obj; + p->path.cmd.append(QQuickPathItemPath::LineTo); + p->path.coords.append(callData->args[0].toNumber()); + p->path.coords.append(callData->args[1].toNumber()); + } + + scope.result = callData->thisObject.asReturnedValue(); +} + +void QQuickPathItemJSPathPrototype::method_quadTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + if (callData->argc >= 4) { + QQuickPathItemPathObject *p = r->d()->obj; + p->path.cmd.append(QQuickPathItemPath::QuadTo); + const QV4::Value *v = callData->args; + p->path.coords.append(v[0].toNumber()); // cx + p->path.coords.append(v[1].toNumber()); // cy + p->path.coords.append(v[2].toNumber()); // x + p->path.coords.append(v[3].toNumber()); // y + } + + scope.result = callData->thisObject.asReturnedValue(); +} + +void QQuickPathItemJSPathPrototype::method_cubicTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + if (callData->argc >= 6) { + QQuickPathItemPathObject *p = r->d()->obj; + p->path.cmd.append(QQuickPathItemPath::CubicTo); + const QV4::Value *v = callData->args; + p->path.coords.append(v[0].toNumber()); // c1x + p->path.coords.append(v[1].toNumber()); // c1y + p->path.coords.append(v[2].toNumber()); // c2x + p->path.coords.append(v[3].toNumber()); // c2y + p->path.coords.append(v[4].toNumber()); // x + p->path.coords.append(v[5].toNumber()); // y + } + + scope.result = callData->thisObject.asReturnedValue(); +} + +void QQuickPathItemJSPathPrototype::method_arcTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + if (callData->argc >= 7) { + QQuickPathItemPathObject *p = r->d()->obj; + p->path.cmd.append(QQuickPathItemPath::ArcTo); + const QV4::Value *v = callData->args; + p->path.coords.append(v[0].toNumber()); // radiusX + p->path.coords.append(v[1].toNumber()); // radiusY + p->path.coords.append(v[2].toNumber()); // xAxisRotation + p->path.coords.append(v[3].toNumber()); // x + p->path.coords.append(v[4].toNumber()); // y + p->path.coords.append(v[5].toNumber()); // sweepFlag + p->path.coords.append(v[6].toNumber()); // largeArc + } + + scope.result = callData->thisObject.asReturnedValue(); +} + +void QQuickPathItemJSStrokeFillParamsPrototype::method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + r->d()->obj->clear(); + + scope.result = callData->thisObject.asReturnedValue(); +} + +extern QColor qt_color_from_string(const QV4::Value &name); // qquickcontext2d.cpp + +static inline QString qt_color_string(const QColor &color) +{ + if (color.alpha() == 255) + return color.name(); + QString alphaString = QString::number(color.alphaF(), 'f'); + while (alphaString.endsWith(QLatin1Char('0'))) + alphaString.chop(1); + if (alphaString.endsWith(QLatin1Char('.'))) + alphaString += QLatin1Char('0'); + return QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString); +} + +void QQuickPathItemJSStrokeFillParams::method_get_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = QV4::Encode(scope.engine->newString(qt_color_string(r->d()->obj->sfp.strokeColor))); +} + +void QQuickPathItemJSStrokeFillParams::method_set_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isString()) + r->d()->obj->sfp.strokeColor = qt_color_from_string(value); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.strokeWidth); +} + +void QQuickPathItemJSStrokeFillParams::method_set_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + r->d()->obj->sfp.strokeWidth = value->toNumber(); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = QV4::Encode(scope.engine->newString(qt_color_string(r->d()->obj->sfp.fillColor))); +} + +void QQuickPathItemJSStrokeFillParams::method_set_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isString()) + r->d()->obj->sfp.fillColor = qt_color_from_string(value); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.fillRule); +} + +void QQuickPathItemJSStrokeFillParams::method_set_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isInt32()) + r->d()->obj->sfp.fillRule = QQuickVisualPath::FillRule(value->integerValue()); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.joinStyle); +} + +void QQuickPathItemJSStrokeFillParams::method_set_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isInt32()) + r->d()->obj->sfp.joinStyle = QQuickVisualPath::JoinStyle(value->integerValue()); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.miterLimit); +} + +void QQuickPathItemJSStrokeFillParams::method_set_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + r->d()->obj->sfp.miterLimit = value->toNumber(); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.capStyle); +} + +void QQuickPathItemJSStrokeFillParams::method_set_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isInt32()) + r->d()->obj->sfp.capStyle = QQuickVisualPath::CapStyle(value->integerValue()); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.strokeStyle); +} + +void QQuickPathItemJSStrokeFillParams::method_set_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isInt32()) + r->d()->obj->sfp.strokeStyle = QQuickVisualPath::StrokeStyle(value->integerValue()); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.dashOffset); +} + +void QQuickPathItemJSStrokeFillParams::method_set_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + r->d()->obj->sfp.dashOffset = value->toNumber(); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedArrayObject a(scope, scope.engine->newArrayObject()); + QQuickPathItemStrokeFillParamsObject *p = r->d()->obj; + a->arrayReserve(p->sfp.dashPattern.count()); + QV4::ScopedValue v(scope); + for (int i = 0; i < p->sfp.dashPattern.count(); ++i) + a->arrayPut(i, (v = scope.engine->fromVariant(p->sfp.dashPattern[i]))); + a->setArrayLengthUnchecked(p->sfp.dashPattern.count()); + + scope.result = a.asReturnedValue(); +} + +void QQuickPathItemJSStrokeFillParams::method_set_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isObject()) { + QV4::Scoped ao(scope, value); + if (!!ao) { + QQuickPathItemStrokeFillParamsObject *p = r->d()->obj; + p->sfp.dashPattern.resize(ao->getLength()); + QV4::ScopedValue val(scope); + for (int i = 0; i < p->sfp.dashPattern.count(); ++i) { + val = ao->getIndexed(i); + p->sfp.dashPattern[i] = val->toNumber(); + } + } + } + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = r->d()->obj->v4fillGradient.value(); +} + +void QQuickPathItemJSStrokeFillParams::method_set_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + QV4::Scoped qobjectWrapper(scope, value); + if (!!qobjectWrapper) { + if (QQuickPathGradient *grad = qobject_cast(qobjectWrapper->object())) { + r->d()->obj->v4fillGradient.set(scope.engine, value); + r->d()->obj->sfp.fillGradient = grad; + } + } else { + r->d()->obj->v4fillGradient.set(scope.engine, nullptr); + r->d()->obj->sfp.fillGradient = nullptr; + } + + scope.result = QV4::Encode::undefined(); +} + +QQuickPathItemJSEngineData::QQuickPathItemJSEngineData(QV4::ExecutionEngine *v4) +{ + QV4::Scope scope(v4); + + QV4::ScopedObject proto(scope, QQuickPathItemJSPathPrototype::create(v4)); + pathProto = proto; + + proto = QV4::ScopedObject(scope, QQuickPathItemJSStrokeFillParamsPrototype::create(v4)); + + proto->defineAccessorProperty(QStringLiteral("strokeColor"), + QQuickPathItemJSStrokeFillParams::method_get_strokeColor, + QQuickPathItemJSStrokeFillParams::method_set_strokeColor); + proto->defineAccessorProperty(QStringLiteral("strokeWidth"), + QQuickPathItemJSStrokeFillParams::method_get_strokeWidth, + QQuickPathItemJSStrokeFillParams::method_set_strokeWidth); + proto->defineAccessorProperty(QStringLiteral("fillColor"), + QQuickPathItemJSStrokeFillParams::method_get_fillColor, + QQuickPathItemJSStrokeFillParams::method_set_fillColor); + proto->defineAccessorProperty(QStringLiteral("fillRule"), + QQuickPathItemJSStrokeFillParams::method_get_fillRule, + QQuickPathItemJSStrokeFillParams::method_set_fillRule); + proto->defineAccessorProperty(QStringLiteral("joinStyle"), + QQuickPathItemJSStrokeFillParams::method_get_joinStyle, + QQuickPathItemJSStrokeFillParams::method_set_joinStyle); + proto->defineAccessorProperty(QStringLiteral("miterLimit"), + QQuickPathItemJSStrokeFillParams::method_get_miterLimit, + QQuickPathItemJSStrokeFillParams::method_set_miterLimit); + proto->defineAccessorProperty(QStringLiteral("capStyle"), + QQuickPathItemJSStrokeFillParams::method_get_capStyle, + QQuickPathItemJSStrokeFillParams::method_set_capStyle); + proto->defineAccessorProperty(QStringLiteral("strokeStyle"), + QQuickPathItemJSStrokeFillParams::method_get_strokeStyle, + QQuickPathItemJSStrokeFillParams::method_set_strokeStyle); + proto->defineAccessorProperty(QStringLiteral("dashOffset"), + QQuickPathItemJSStrokeFillParams::method_get_dashOffset, + QQuickPathItemJSStrokeFillParams::method_set_dashOffset); + proto->defineAccessorProperty(QStringLiteral("dashPattern"), + QQuickPathItemJSStrokeFillParams::method_get_dashPattern, + QQuickPathItemJSStrokeFillParams::method_set_dashPattern); + proto->defineAccessorProperty(QStringLiteral("fillGradient"), + QQuickPathItemJSStrokeFillParams::method_get_fillGradient, + QQuickPathItemJSStrokeFillParams::method_set_fillGradient); + + strokeFillParamsProto = proto; +} + +void QQuickPathItemPathObject::setV4Engine(QV4::ExecutionEngine *engine) +{ + QQuickPathItemJSEngineData *ed = engineData(engine); + QV4::Scope scope(engine); + QV4::Scoped wrapper(scope, engine->memoryManager->allocObject()); + QV4::ScopedObject p(scope, ed->pathProto.value()); + wrapper->setPrototype(p); + wrapper->d()->obj = this; + m_v4value = wrapper; +} + +void QQuickPathItemPathObject::clear() +{ + path = QQuickPathItemPath(); +} + +void QQuickPathItemStrokeFillParamsObject::clear() +{ + sfp = QQuickPathItemStrokeFillParams(); + if (!v4fillGradient.isNullOrUndefined()) + v4fillGradient.set(v4fillGradient.engine(), nullptr); +} + +void QQuickPathItemStrokeFillParamsObject::setV4Engine(QV4::ExecutionEngine *engine) +{ + QQuickPathItemJSEngineData *ed = engineData(engine); + QV4::Scope scope(engine); + QV4::Scoped wrapper(scope, engine->memoryManager->allocObject()); + QV4::ScopedObject p(scope, ed->strokeFillParamsProto.value()); + wrapper->setPrototype(p); + wrapper->d()->obj = this; + m_v4value = wrapper; +} + +void QQuickPathItem::newPath(QQmlV4Function *args) +{ + QQuickPathItemPathObject *obj = new QQuickPathItemPathObject(this); + obj->setV4Engine(QQmlEnginePrivate::get(qmlEngine(this))->v4engine()); + args->setReturnValue(obj->v4value()); +} + +void QQuickPathItem::newStrokeFillParams(QQmlV4Function *args) +{ + QQuickPathItemStrokeFillParamsObject *obj = new QQuickPathItemStrokeFillParamsObject(this); + obj->setV4Engine(QQmlEnginePrivate::get(qmlEngine(this))->v4engine()); + args->setReturnValue(obj->v4value()); +} + +void QQuickPathItem::clearVisualPaths(QQmlV4Function *args) +{ + Q_UNUSED(args); + Q_D(QQuickPathItem); + d->jsData.paths.clear(); + d->jsData.sfp.clear(); +} + +void QQuickPathItem::commitVisualPaths(QQmlV4Function *args) +{ + Q_UNUSED(args); + Q_D(QQuickPathItem); + d->_q_visualPathChanged(); +} + +void QQuickPathItem::appendVisualPath(QQmlV4Function *args) +{ + if (args->length() < 2) + return; + + Q_D(QQuickPathItem); + QV4::Scope scope(args->v4engine()); + QV4::Scoped jsp(scope, (*args)[0]); + QV4::Scoped jssfp(scope, (*args)[1]); + + const QQuickPathItemPath &path(jsp->d()->obj->path); + const QQuickPathItemStrokeFillParams &sfp(jssfp->d()->obj->sfp); + + d->jsData.paths.append(path); + d->jsData.sfp.append(sfp); +} + QT_END_NAMESPACE #include "moc_qquickpathitem_p.cpp" diff --git a/src/quick/items/qquickpathitem_p.h b/src/quick/items/qquickpathitem_p.h index 9ca1418d9e..991ab7a2bf 100644 --- a/src/quick/items/qquickpathitem_p.h +++ b/src/quick/items/qquickpathitem_p.h @@ -55,6 +55,7 @@ #include #include +#include #include QT_REQUIRE_CONFIG(quick_path); @@ -296,6 +297,12 @@ public: QQmlListProperty elements(); + Q_INVOKABLE void newPath(QQmlV4Function *args); + Q_INVOKABLE void newStrokeFillParams(QQmlV4Function *args); + Q_INVOKABLE void clearVisualPaths(QQmlV4Function *args); + Q_INVOKABLE void commitVisualPaths(QQmlV4Function *args); + Q_INVOKABLE void appendVisualPath(QQmlV4Function *args); + protected: QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override; void updatePolish() override; diff --git a/src/quick/items/qquickpathitem_p_p.h b/src/quick/items/qquickpathitem_p_p.h index 3c63ec6dc2..091a453c0b 100644 --- a/src/quick/items/qquickpathitem_p_p.h +++ b/src/quick/items/qquickpathitem_p_p.h @@ -62,6 +62,39 @@ QT_BEGIN_NAMESPACE class QSGPlainTexture; +struct QQuickPathItemPath +{ + enum Command { + MoveTo, + LineTo, + QuadTo, + CubicTo, + ArcTo + }; + + QVector cmd; + QVector coords; + + QPainterPath toPainterPath() const; +}; + +struct QQuickPathItemStrokeFillParams +{ + QQuickPathItemStrokeFillParams(); + + QColor strokeColor; + qreal strokeWidth; + QColor fillColor; + QQuickVisualPath::FillRule fillRule; + QQuickVisualPath::JoinStyle joinStyle; + int miterLimit; + QQuickVisualPath::CapStyle capStyle; + QQuickVisualPath::StrokeStyle strokeStyle; + qreal dashOffset; + QVector dashPattern; + QQuickPathGradient *fillGradient; +}; + class QQuickAbstractPathRenderer { public: @@ -74,7 +107,14 @@ public: // Gui thread virtual void beginSync(int totalCount) = 0; + virtual void endSync(bool async) = 0; + virtual void setAsyncCallback(void (*)(void *), void *) { } + virtual Flags flags() const { return 0; } + // - QML API virtual void setPath(int index, const QQuickPath *path) = 0; + // - JS API + virtual void setJSPath(int index, const QQuickPathItemPath &path) = 0; + // - stroke/fill parameters 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; @@ -84,9 +124,6 @@ public: virtual void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, qreal dashOffset, const QVector &dashPattern) = 0; virtual void setFillGradient(int index, QQuickPathGradient *gradient) = 0; - virtual void endSync(bool async) = 0; - virtual void setAsyncCallback(void (*)(void *), void *) { } - virtual Flags flags() const { return 0; } // Render thread, with gui blocked virtual void updateNode() = 0; @@ -121,17 +158,7 @@ public: QQuickPath *path; int dirty; - QColor strokeColor; - qreal strokeWidth; - QColor fillColor; - QQuickVisualPath::FillRule fillRule; - QQuickVisualPath::JoinStyle joinStyle; - int miterLimit; - QQuickVisualPath::CapStyle capStyle; - QQuickVisualPath::StrokeStyle strokeStyle; - qreal dashOffset; - QVector dashPattern; - QQuickPathGradient *fillGradient; + QQuickPathItemStrokeFillParams sfp; }; class QQuickPathItemPrivate : public QQuickItemPrivate @@ -157,7 +184,53 @@ public: bool async; QQuickPathItem::Status status; QQuickAbstractPathRenderer *renderer; - QVector vp; + + struct { + QVector vp; + } qmlData; + + struct { + bool isValid() const { Q_ASSERT(paths.count() == sfp.count()); return !paths.isEmpty(); } + QVector paths; + QVector sfp; + } jsData; +}; + +class QQuickPathItemPathObject : public QObject +{ + Q_OBJECT + +public: + QQuickPathItemPathObject(QObject *parent = nullptr) : QObject(parent) { } + + void setV4Engine(QV4::ExecutionEngine *engine); + QV4::ReturnedValue v4value() const { return m_v4value.value(); } + + QQuickPathItemPath path; + + void clear(); + +private: + QV4::PersistentValue m_v4value; +}; + +class QQuickPathItemStrokeFillParamsObject : public QObject +{ + Q_OBJECT + +public: + QQuickPathItemStrokeFillParamsObject(QObject *parent = nullptr) : QObject(parent) { } + + void setV4Engine(QV4::ExecutionEngine *engine); + QV4::ReturnedValue v4value() const { return m_v4value.value(); } + + QQuickPathItemStrokeFillParams sfp; + QV4::PersistentValue v4fillGradient; + + void clear(); + +private: + QV4::PersistentValue m_v4value; }; #ifndef QT_NO_OPENGL diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp index 3de01a5bc7..a76a5e2b43 100644 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -178,6 +178,13 @@ void QQuickPathItemGenericRenderer::setPath(int index, const QQuickPath *path) d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom; } +void QQuickPathItemGenericRenderer::setJSPath(int index, const QQuickPathItemPath &path) +{ + VisualPathData &d(m_vp[index]); + d.path = path.toPainterPath(); + d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom; +} + void QQuickPathItemGenericRenderer::setStrokeColor(int index, const QColor &color) { VisualPathData &d(m_vp[index]); diff --git a/src/quick/items/qquickpathitemgenericrenderer_p.h b/src/quick/items/qquickpathitemgenericrenderer_p.h index 045c52d610..70a9e88d2f 100644 --- a/src/quick/items/qquickpathitemgenericrenderer_p.h +++ b/src/quick/items/qquickpathitemgenericrenderer_p.h @@ -87,6 +87,7 @@ public: void beginSync(int totalCount) override; void setPath(int index, const QQuickPath *path) override; + void setJSPath(int index, const QQuickPathItemPath &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; diff --git a/src/quick/items/qquickpathitemnvprrenderer.cpp b/src/quick/items/qquickpathitemnvprrenderer.cpp index 13fab2dc76..f8504f9985 100644 --- a/src/quick/items/qquickpathitemnvprrenderer.cpp +++ b/src/quick/items/qquickpathitemnvprrenderer.cpp @@ -62,6 +62,14 @@ void QQuickPathItemNvprRenderer::setPath(int index, const QQuickPath *path) m_accDirty |= DirtyPath; } +void QQuickPathItemNvprRenderer::setJSPath(int index, const QQuickPathItemPath &path) +{ + VisualPathGuiData &d(m_vp[index]); + convertJSPath(path, &d); + d.dirty |= DirtyPath; + m_accDirty |= DirtyPath; +} + void QQuickPathItemNvprRenderer::setStrokeColor(int index, const QColor &color) { VisualPathGuiData &d(m_vp[index]); @@ -288,6 +296,82 @@ void QQuickPathItemNvprRenderer::convertPath(const QQuickPath *path, VisualPathG d->path.cmd.append(GL_CLOSE_PATH_NV); } +void QQuickPathItemNvprRenderer::convertJSPath(const QQuickPathItemPath &path, VisualPathGuiData *d) +{ + d->path = NvprPath(); + if (path.cmd.isEmpty()) + return; + + QPointF startPos(0, 0); + QPointF pos(startPos); + int coordIdx = 0; + + for (QQuickPathItemPath::Command cmd : path.cmd) { + switch (cmd) { + case QQuickPathItemPath::MoveTo: + d->path.cmd.append(GL_MOVE_TO_NV); + pos = QPointF(path.coords[coordIdx], path.coords[coordIdx + 1]); + startPos = pos; + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 2; + break; + case QQuickPathItemPath::LineTo: + d->path.cmd.append(GL_LINE_TO_NV); + pos = QPointF(path.coords[coordIdx], path.coords[coordIdx + 1]); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 2; + break; + case QQuickPathItemPath::QuadTo: + d->path.cmd.append(GL_QUADRATIC_CURVE_TO_NV); + d->path.coord.append(path.coords[coordIdx]); + d->path.coord.append(path.coords[coordIdx + 1]); + pos = QPointF(path.coords[coordIdx + 2], path.coords[coordIdx + 3]); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 4; + break; + case QQuickPathItemPath::CubicTo: + d->path.cmd.append(GL_CUBIC_CURVE_TO_NV); + d->path.coord.append(path.coords[coordIdx]); + d->path.coord.append(path.coords[coordIdx + 1]); + d->path.coord.append(path.coords[coordIdx + 2]); + d->path.coord.append(path.coords[coordIdx + 3]); + pos = QPointF(path.coords[coordIdx + 4], path.coords[coordIdx + 5]); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 6; + break; + case QQuickPathItemPath::ArcTo: + { + const bool sweepFlag = !qFuzzyIsNull(path.coords[coordIdx + 5]); + const bool useLargeArc = !qFuzzyIsNull(path.coords[coordIdx + 6]); + GLenum cmd; + if (useLargeArc) + cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV; + else + cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV; + d->path.cmd.append(cmd); + d->path.coord.append(path.coords[coordIdx]); // rx + d->path.coord.append(path.coords[coordIdx + 1]); // ry + d->path.coord.append(path.coords[coordIdx + 2]); // xrot + pos = QPointF(path.coords[coordIdx + 3], path.coords[coordIdx + 4]); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 7; + } + break; + default: + qWarning("Unknown JS path command: %d", cmd); + break; + } + } + + if (pos == startPos) + d->path.cmd.append(GL_CLOSE_PATH_NV); +} + static inline QVector4D qsg_premultiply(const QColor &c, float globalOpacity) { const float o = c.alphaF() * globalOpacity; diff --git a/src/quick/items/qquickpathitemnvprrenderer_p.h b/src/quick/items/qquickpathitemnvprrenderer_p.h index 61f8b5ebb9..deab9cf7f9 100644 --- a/src/quick/items/qquickpathitemnvprrenderer_p.h +++ b/src/quick/items/qquickpathitemnvprrenderer_p.h @@ -81,6 +81,7 @@ public: void beginSync(int totalCount) override; void setPath(int index, const QQuickPath *path) override; + void setJSPath(int index, const QQuickPathItemPath &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; @@ -121,6 +122,7 @@ private: }; void convertPath(const QQuickPath *path, VisualPathGuiData *d); + void convertJSPath(const QQuickPathItemPath &path, VisualPathGuiData *d); QQuickPathItemNvprRenderNode *m_node = nullptr; int m_accDirty = 0; diff --git a/src/quick/items/qquickpathitemsoftwarerenderer.cpp b/src/quick/items/qquickpathitemsoftwarerenderer.cpp index 46ebcbfe6d..b7aa93bf65 100644 --- a/src/quick/items/qquickpathitemsoftwarerenderer.cpp +++ b/src/quick/items/qquickpathitemsoftwarerenderer.cpp @@ -58,6 +58,14 @@ void QQuickPathItemSoftwareRenderer::setPath(int index, const QQuickPath *path) m_accDirty |= DirtyPath; } +void QQuickPathItemSoftwareRenderer::setJSPath(int index, const QQuickPathItemPath &path) +{ + VisualPathGuiData &d(m_vp[index]); + d.path = path.toPainterPath(); + d.dirty |= DirtyPath; + m_accDirty |= DirtyPath; +} + void QQuickPathItemSoftwareRenderer::setStrokeColor(int index, const QColor &color) { VisualPathGuiData &d(m_vp[index]); diff --git a/src/quick/items/qquickpathitemsoftwarerenderer_p.h b/src/quick/items/qquickpathitemsoftwarerenderer_p.h index 64280b436e..e76590bdfe 100644 --- a/src/quick/items/qquickpathitemsoftwarerenderer_p.h +++ b/src/quick/items/qquickpathitemsoftwarerenderer_p.h @@ -73,6 +73,7 @@ public: void beginSync(int totalCount) override; void setPath(int index, const QQuickPath *path) override; + void setJSPath(int index, const QQuickPathItemPath &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; -- cgit v1.2.3 From d55f0433c102dbdbd0b69c8ddbc397a0854d86a0 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 17 Mar 2017 14:18:40 +0100 Subject: Fix for disappearing VisualPath entries in generic PathItem Prevent internal overflows in tesselation due to 100x scale and keep the correct scenegraph node - VisualPath entry associations. Change-Id: I181eae6872ea5c5af5783b68a8757a5249c074e5 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemgenericrenderer.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp index a76a5e2b43..dfa01f4d42 100644 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -51,7 +51,7 @@ QT_BEGIN_NAMESPACE -static const qreal SCALE = 100; +static const qreal TRI_SCALE = 1; struct ColoredVertex // must match QSGGeometry::ColoredPoint2D { @@ -410,13 +410,13 @@ void QQuickPathItemGenericRenderer::triangulateFill(const QPainterPath &path, { const QVectorPath &vp = qtVectorPathForPath(path); - QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(SCALE, SCALE), 1, supportsElementIndexUint); + QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(TRI_SCALE, TRI_SCALE), 1, supportsElementIndexUint); const int vertexCount = ts.vertices.count() / 2; // just a qreal vector with x,y hence the / 2 fillVertices->resize(vertexCount); ColoredVertex *vdst = reinterpret_cast(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, fillColor); + vdst[i].set(vsrc[i * 2] / TRI_SCALE, vsrc[i * 2 + 1] / TRI_SCALE, fillColor); size_t indexByteSize; if (ts.indices.type() == QVertexIndexVector::UnsignedShort) { @@ -441,7 +441,7 @@ void QQuickPathItemGenericRenderer::triangulateStroke(const QPainterPath &path, { const QVectorPath &vp = qtVectorPathForPath(path); const QRectF clip(QPointF(0, 0), clipSize); - const qreal inverseScale = 1.0 / SCALE; + const qreal inverseScale = 1.0 / TRI_SCALE; QTriangulatingStroker stroker; stroker.setInvScale(inverseScale); @@ -508,8 +508,12 @@ void QQuickPathItemGenericRenderer::updateNode() if (m_accDirty & DirtyList) d.effectiveDirty |= DirtyFillGeom | DirtyStrokeGeom | DirtyColor | DirtyFillGradient; - if (!d.effectiveDirty) + + if (!d.effectiveDirty) { + prevNode = node; + nodePtr = &node->m_next; continue; + } if (d.fillColor.a == 0) { delete node->m_fillNode; -- cgit v1.2.3 From 55970b212fff45161f94ffe637b49473e8f5925b Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 27 Mar 2017 13:29:04 +0200 Subject: Fix async PathItem results not always showing up Change-Id: Ie8eb1dd5ea8a560f8160384637cc5d76e3bb7c60 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemgenericrenderer.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp index dfa01f4d42..7a259617dd 100644 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -642,6 +642,12 @@ void QQuickPathItemGenericRenderer::updateStrokeNode(VisualPathData *d, QQuickPa n->markDirty(QSGNode::DirtyGeometry); + // Async loading runs update once, bails out above, then updates again once + // ready. Set the material dirty then. This is in-line with fill where the + // first activateMaterial() achieves the same. + if (!g->vertexCount()) + n->markDirty(QSGNode::DirtyMaterial); + if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyStrokeGeom)) { ColoredVertex *vdst = reinterpret_cast(g->vertexData()); for (int i = 0; i < g->vertexCount(); ++i) -- cgit v1.2.3 From 83fc08cc6faa5f52a010d7bd821c9606f13d5ae9 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 27 Mar 2017 15:37:29 +0200 Subject: PathItem docs for the declarative API Also add the missing property for disabling vendor extensions like NVPR. Change-Id: Id7dfe245305c8ecdfad87359894e7f496c4100d0 Reviewed-by: Andy Nichols --- src/quick/doc/images/pathitem-code-example.png | Bin 0 -> 5989 bytes src/quick/doc/images/visualpath-code-example.png | Bin 0 -> 844 bytes src/quick/items/qquickpathitem.cpp | 457 ++++++++++++++++++++++- src/quick/items/qquickpathitem_p.h | 5 + src/quick/items/qquickpathitem_p_p.h | 2 + 5 files changed, 447 insertions(+), 17 deletions(-) create mode 100644 src/quick/doc/images/pathitem-code-example.png create mode 100644 src/quick/doc/images/visualpath-code-example.png (limited to 'src') diff --git a/src/quick/doc/images/pathitem-code-example.png b/src/quick/doc/images/pathitem-code-example.png new file mode 100644 index 0000000000..25dbe8b311 Binary files /dev/null and b/src/quick/doc/images/pathitem-code-example.png differ diff --git a/src/quick/doc/images/visualpath-code-example.png b/src/quick/doc/images/visualpath-code-example.png new file mode 100644 index 0000000000..429e85aa32 Binary files /dev/null and b/src/quick/doc/images/visualpath-code-example.png differ diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp index 61216be9b0..b14fc1caba 100644 --- a/src/quick/items/qquickpathitem.cpp +++ b/src/quick/items/qquickpathitem.cpp @@ -114,6 +114,60 @@ QPainterPath QQuickPathItemPath::toPainterPath() const return p; } +/*! + \qmltype VisualPath + \instantiates QQuickVisualPath + \inqmlmodule QtQuick + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Object + \brief Describes a Path and associated properties for stroking and filling + \since 5.10 + + A PathItem contains one or more VisualPath elements. At least one + VisualPath is necessary in order to have a PathItem output anything + visible. A VisualPath in turn contains a Path and properties describing the + stroking and filling parameters, such as the stroke width and color, the + fill color or gradient, join and cap styles, and so on. Finally, the Path + object contains a list of path elements like PathMove, PathLine, PathCubic, + PathQuad, PathArc. + + Any property changes in these data sets will be bubble up and change the + output of the PathItem. This means that it is simple and easy to change, or + even animate, the starting and ending position, control points, or any + stroke or fill parameters using the usual QML bindings and animation types + like NumberAnimation. + + In the following example the line join style changes automatically based on + the value of joinStyleIndex: + + \code + VisualPath { + strokeColor: "black" + strokeWidth: 16 + fillColor: "transparent" + capStyle: VisualPath.RoundCap + + property int joinStyleIndex: 0 + property variant styles: [ VisualPath.BevelJoin, VisualPath.MiterJoin, VisualPath.RoundJoin ] + + joinStyle: styles[joinStyleIndex] + + Path { + startX: 30 + startY: 30 + PathLine { x: 100; y: 100 } + PathLine { x: 30; y: 100 } + } + } + \endcode + + Once associated with a PathItem, here is the output with a joinStyleIndex + of 2 (VisualPath.RoundJoin): + + \image visualpath-code-example.png + */ + QQuickVisualPathPrivate::QQuickVisualPathPrivate() : path(nullptr), dirty(DirtyAll) @@ -129,6 +183,14 @@ QQuickVisualPath::~QQuickVisualPath() { } +/*! + \qmlproperty Path QtQuick::VisualPath::path + + This property holds the Path object. + + \default + */ + QQuickPath *QQuickVisualPath::path() const { Q_D(const QQuickVisualPath); @@ -160,6 +222,16 @@ void QQuickVisualPathPrivate::_q_pathChanged() emit q->changed(); } +/*! + \qmlproperty color QtQuick::VisualPath::strokeColor + + This property holds the stroking color. + + When set to \c transparent, no stroking occurs. + + The default value is \c white. + */ + QColor QQuickVisualPath::strokeColor() const { Q_D(const QQuickVisualPath); @@ -177,6 +249,16 @@ void QQuickVisualPath::setStrokeColor(const QColor &color) } } +/*! + \qmlproperty color QtQuick::VisualPath::strokeWidth + + This property holds the stroke width. + + When set to a negative value, no stroking occurs. + + The default value is 1. + */ + qreal QQuickVisualPath::strokeWidth() const { Q_D(const QQuickVisualPath); @@ -194,6 +276,16 @@ void QQuickVisualPath::setStrokeWidth(qreal w) } } +/*! + \qmlproperty color QtQuick::VisualPath::fillColor + + This property holds the fill color. + + When set to \c transparent, no filling occurs. + + The default value is \c white. + */ + QColor QQuickVisualPath::fillColor() const { Q_D(const QQuickVisualPath); @@ -211,6 +303,19 @@ void QQuickVisualPath::setFillColor(const QColor &color) } } +/*! + \qmlproperty enumeration QtQuick::VisualPath::fillRule + + This property holds the fill rule. The default value is + VisualPath.OddEvenFill. For an example on fill rules, see + QPainterPath::setFillRule(). + + \list + \li VisualPath.OddEvenFill + \li VisualPath.WindingFill + \endlist + */ + QQuickVisualPath::FillRule QQuickVisualPath::fillRule() const { Q_D(const QQuickVisualPath); @@ -228,6 +333,19 @@ void QQuickVisualPath::setFillRule(FillRule fillRule) } } +/*! + \qmlproperty enumeration QtQuick::VisualPath::joinStyle + + This property defines how joins between two connected lines are drawn. The + default value is VisualPath.BevelJoin. + + \list + \li VisualPath.MiterJoin - The outer edges of the lines are extended to meet at an angle, and this area is filled. + \li VisualPath.BevelJoin - The triangular notch between the two lines is filled. + \li VisualPath.RoundJoin - A circular arc between the two lines is filled. + \endlist + */ + QQuickVisualPath::JoinStyle QQuickVisualPath::joinStyle() const { Q_D(const QQuickVisualPath); @@ -245,6 +363,15 @@ void QQuickVisualPath::setJoinStyle(JoinStyle style) } } +/*! + \qmlproperty int QtQuick::VisualPath::miterLimit + + When VisualPath.joinStyle is set to VisualPath.MiterJoin, this property + specifies how far the miter join can extend from the join point. + + The default value is 2. + */ + int QQuickVisualPath::miterLimit() const { Q_D(const QQuickVisualPath); @@ -262,6 +389,19 @@ void QQuickVisualPath::setMiterLimit(int limit) } } +/*! + \qmlproperty enumeration QtQuick::VisualPath::capStyle + + This property defines how the end points of lines are drawn. The + default value is VisualPath.SquareCap. + + \list + \li VisualPath.FlatCap - A square line end that does not cover the end point of the line. + \li VisualPath.SquareCap - A square line end that covers the end point and extends beyond it by half the line width. + \li VisualPath.RoundCap - A rounded line end. + \endlist + */ + QQuickVisualPath::CapStyle QQuickVisualPath::capStyle() const { Q_D(const QQuickVisualPath); @@ -279,6 +419,18 @@ void QQuickVisualPath::setCapStyle(CapStyle style) } } +/*! + \qmlproperty enumeration QtQuick::VisualPath::strokeStyle + + This property defines the style of stroking. The default value is + VisualPath.SolidLine. + + \list + \li VisualPath.SolidLine - A plain line. + \li VisualPath.DashLine - Dashes separated by a few pixels. + \endlist + */ + QQuickVisualPath::StrokeStyle QQuickVisualPath::strokeStyle() const { Q_D(const QQuickVisualPath); @@ -296,6 +448,17 @@ void QQuickVisualPath::setStrokeStyle(StrokeStyle style) } } +/*! + \qmlproperty real QtQuick::VisualPath::dashOffset + + This property defines the starting point on the dash pattern, measured in + units used to specify the dash pattern. + + The default value is 0. + + \sa QPen::setDashOffset() + */ + qreal QQuickVisualPath::dashOffset() const { Q_D(const QQuickVisualPath); @@ -313,6 +476,20 @@ void QQuickVisualPath::setDashOffset(qreal offset) } } +/*! + \qmlproperty list QtQuick::VisualPath::dashPattern + + This property defines the dash pattern when VisualPath.strokeStyle is set + to VisualPath.DashLine. The pattern must be specified as an even number of + positive entries where the entries 1, 3, 5... are the dashes and 2, 4, 6... + are the spaces. The pattern is specified in units of the pen's width. + + The default value is (4, 2), meaning a dash of 4 * VisualPath.strokeWidth + pixels followed by a space of 2 * VisualPath.strokeWidth pixels. + + \sa QPen::setDashPattern() + */ + QVector QQuickVisualPath::dashPattern() const { Q_D(const QQuickVisualPath); @@ -330,6 +507,17 @@ void QQuickVisualPath::setDashPattern(const QVector &array) } } +/*! + \qmlproperty PathGradient QtQuick::VisualPath::fillGradient + + This property defines the fill gradient. By default no gradient is enabled + and the value is \c null. In this case the fill uses a solid color based on + the value of VisuaLPath.fillColor. + + When set, VisualPath.fillColor is ignored and filling is done using one of + the PathGradient subtypes. + */ + QQuickPathGradient *QQuickVisualPath::fillGradient() const { Q_D(const QQuickVisualPath); @@ -375,15 +563,56 @@ void QQuickVisualPath::resetFillGradient() \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}. + triangulation or by using a GPU vendor 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 + PathItem is suitable for creating shapes spreading over larger areas of the screen, avoiding the performance penalty for texture uploads or framebuffer - blits. + blits. In addition, the declarative API allows manipulating, binding to, + and even animating the path element properties like starting and ending + position, the control points, etc. + + 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, PathSvg. + + See \l Path for a detailed overview of the supported path elements. + + \code + PathItem { + width: 200 + height: 150 + anchors.centerIn: parent + VisualPath { + strokeWidth: 4 + strokeColor: "red" + fillGradient: PathLinearGradient { + x1: 20; y1: 20 + x2: 180; y2: 130 + PathGradientStop { position: 0; color: "blue" } + PathGradientStop { position: 0.2; color: "green" } + PathGradientStop { position: 0.4; color: "red" } + PathGradientStop { position: 0.6; color: "yellow" } + PathGradientStop { position: 1; color: "cyan" } + } + strokeStyle: VisualPath.DashLine + dashPattern: [ 1, 4 ] + Path { + startX: 20; startY: 20 + PathLine { x: 180; y: 130 } + PathLine { x: 20; y: 130 } + PathLine { x: 20; y: 20 } + } + } + } + \endcode + + \image pathitem-code-example.png - Nonetheless it is important to be aware of performance implications, in + \note 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 @@ -396,18 +625,25 @@ void QQuickVisualPath::resetFillGradient() \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. + The following list summarizes the available PathItem rendering approaches: - \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. + \list - See \l Path for a detailed overview of the supported path elements. + \li When running with the default, OpenGL backend of Qt Quick, both the + generic, triangulation-based and the NVIDIA-specific + \c{GL_NV_path_rendering} methods are available. The choice is made at + runtime, depending on the graphics driver's capabilities. When this is not + desired, applications can force using the generic method by setting the + PathItem.enableVendorExtensions property to \c false. + + \li The \c software backend is fully supported. The path is rendered via + QPainter::strokePath() and QPainter::fillPath() in this case. + + \li The Direct 3D 12 backend is not currently supported. + + \li The OpenVG backend is not currently supported. + + \endlist \sa Path, PathMove, PathLine, PathQuad, PathCubic, PathArc, PathSvg */ @@ -418,7 +654,8 @@ QQuickPathItemPrivate::QQuickPathItemPrivate() rendererType(QQuickPathItem::UnknownRenderer), async(false), status(QQuickPathItem::Null), - renderer(nullptr) + renderer(nullptr), + enableVendorExts(true) { } @@ -453,12 +690,54 @@ QQuickPathItem::~QQuickPathItem() { } +/*! + \qmlproperty enumeration QtQuick::PathItem::rendererType + + This property determines which path rendering backend is active. + + \list + + \li PathItem.UnknownRenderer - The renderer is unknown. + + \li PathItem.GeometryRenderer - The generic, driver independent solution + for OpenGL. Uses the same CPU-based triangulation approach as QPainter's + OpenGL 2 paint engine. This is the default on non-NVIDIA hardware when the + default, OpenGL Qt Quick scenegraph backend is in use. + + \li PathItem.NvprRenderer - Path items are rendered by performing OpenGL + calls using the \c{GL_NV_path_rendering} extension. This is the default on + NVIDIA hardware when the default, OpenGL Qt Quick scenegraph backend is in + use. + + \li PathItem.SoftwareRenderer - Pure QPainter drawing using the raster + paint engine. This is the default, and only, option when the Qt Quick + scenegraph is running with the \c software backend. + + \endlist +*/ + QQuickPathItem::RendererType QQuickPathItem::rendererType() const { Q_D(const QQuickPathItem); return d->rendererType; } +/*! + \qmlproperty bool QtQuick::PathItem::asynchronous + + When PathItem.rendererType is PathItem.GeometryRenderer, the input path is + triangulated on the CPU during the polishing phase of the PathItem. This is + potentially expensive. To offload this work to separate worker threads, set + this property to \c true. + + When enabled, making a PathItem visible will not wait for the content to + become available. Instead, the gui/main thread is not blocked and the + results of the path rendering are shown only when all the asynchronous work + has been finished. + + The default value is \c false. + */ + bool QQuickPathItem::asynchronous() const { Q_D(const QQuickPathItem); @@ -476,6 +755,49 @@ void QQuickPathItem::setAsynchronous(bool async) } } +/*! + \qmlproperty bool QtQuick::PathItem::enableVendorExtensions + + This property controls the usage of non-standard OpenGL extensions like + GL_NV_path_rendering. To disable PathItem.NvprRenderer and force a uniform + behavior regardless of the graphics card and drivers, set this property to + \c false. + + The default value is \c true. + */ + +bool QQuickPathItem::enableVendorExtensions() const +{ + Q_D(const QQuickPathItem); + return d->enableVendorExts; +} + +void QQuickPathItem::setEnableVendorExtensions(bool enable) +{ + Q_D(QQuickPathItem); + if (d->enableVendorExts != enable) { + d->enableVendorExts = enable; + emit enableVendorExtensionsChanged(); + } +} + +/*! + \qmlproperty enumeration QtQuick::PathItem::status + + This property determines the status of the PathItem and is relevant when + PathItem.asynchronous is set to \c true. + + \list + + \li PathItem.Null - Not yet initialized. + + \li PathItem.Ready - The PathItem has finished processing. + + \li PathItem.Processing - The path is being processed. + + \endlist + */ + QQuickPathItem::Status QQuickPathItem::status() const { Q_D(const QQuickPathItem); @@ -520,6 +842,15 @@ static void vpe_clear(QQmlListProperty *property) d->_q_visualPathChanged(); } +/*! + \qmlproperty list QtQuick::PathItem::elements + + This property holds the VisualPath objects that define the contents of the + PathItem. + + \default + */ + QQmlListProperty QQuickPathItem::elements() { return QQmlListProperty(this, @@ -608,7 +939,7 @@ void QQuickPathItemPrivate::createRenderer() switch (ri->graphicsApi()) { #ifndef QT_NO_OPENGL case QSGRendererInterface::OpenGL: - if (QQuickPathItemNvprRenderNode::isSupported()) { + if (enableVendorExts && QQuickPathItemNvprRenderNode::isSupported()) { rendererType = QQuickPathItem::NvprRenderer; renderer = new QQuickPathItemNvprRenderer; } else { @@ -641,7 +972,7 @@ QSGNode *QQuickPathItemPrivate::createNode() switch (ri->graphicsApi()) { #ifndef QT_NO_OPENGL case QSGRendererInterface::OpenGL: - if (QQuickPathItemNvprRenderNode::isSupported()) { + if (enableVendorExts && QQuickPathItemNvprRenderNode::isSupported()) { node = new QQuickPathItemNvprRenderNode; static_cast(renderer)->setNode( static_cast(node)); @@ -740,6 +1071,17 @@ void QQuickPathItemPrivate::sync() // ***** gradient support ***** +/*! + \qmltype PathGradientStop + \instantiates QQuickPathGradientStop + \inqmlmodule QtQuick + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Object + \brief Defines a color at a position in a gradient + \since 5.10 + */ + QQuickPathGradientStop::QQuickPathGradientStop(QObject *parent) : QObject(parent), m_position(0), @@ -747,6 +1089,15 @@ QQuickPathGradientStop::QQuickPathGradientStop(QObject *parent) { } +/*! + \qmlproperty real QtQuick::PathGradientStop::position + + The position and color properties describe the color used at a given + position in a gradient, as represented by a gradient stop. + + The default value is 0. + */ + qreal QQuickPathGradientStop::position() const { return m_position; @@ -761,6 +1112,15 @@ void QQuickPathGradientStop::setPosition(qreal position) } } +/*! + \qmlproperty real QtQuick::PathGradientStop::color + + The position and color properties describe the color used at a given + position in a gradient, as represented by a gradient stop. + + The default value is \c black. + */ + QColor QQuickPathGradientStop::color() const { return m_color; @@ -775,6 +1135,20 @@ void QQuickPathGradientStop::setColor(const QColor &color) } } +/*! + \qmltype PathGradient + \instantiates QQuickPathGradient + \inqmlmodule QtQuick + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Object + \brief Base type of PathItem fill gradients + \since 5.10 + + This is an abstract base class for gradients like PathLinearGradient and + cannot be created directly. + */ + QQuickPathGradient::QQuickPathGradient(QObject *parent) : QObject(parent), m_spread(PadSpread) @@ -794,6 +1168,14 @@ void QQuickPathGradient::appendStop(QQmlListProperty *list, QObject *st grad->m_stops.append(sstop); } +/*! + \qmlproperty list QtQuick::PathGradient::stops + \default + + The list of PathGradientStop objects defining the colors at given positions + in the gradient. + */ + QQmlListProperty QQuickPathGradient::stops() { return QQmlListProperty(this, nullptr, &QQuickPathGradient::appendStop, nullptr, nullptr, nullptr); @@ -812,6 +1194,19 @@ QGradientStops QQuickPathGradient::sortedGradientStops() const return result; } +/*! + \qmlproperty enumeration QtQuick::PathGradient::spred + + Specifies how the area outside the gradient area should be filled. The + default value is PathGradient.PadSpread. + + \list + \li PathGradient.PadSpread - The area is filled with the closest stop color. + \li PathGradient.RepeatSpread - The gradient is repeated outside the gradient area. + \li PathGradient.ReflectSpread - The gradient is reflected outside the gradient area. + \endlist + */ + QQuickPathGradient::SpreadMode QQuickPathGradient::spread() const { return m_spread; @@ -826,11 +1221,39 @@ void QQuickPathGradient::setSpread(SpreadMode mode) } } +/*! + \qmltype PathLinearGradient + \instantiates QQuickPathLinearGradient + \inqmlmodule QtQuick + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits PathGradient + \brief Linear gradient + \since 5.10 + + Linear gradients interpolate colors between start and end points. Outside + these points the gradient is either padded, reflected or repeated depending + on the spread type. + + \sa QLinearGradient + */ + QQuickPathLinearGradient::QQuickPathLinearGradient(QObject *parent) : QQuickPathGradient(parent) { } +/*! + \qmlproperty real QtQuick::PathLinearGradient::x1 + \qmlproperty real QtQuick::PathLinearGradient::y1 + \qmlproperty real QtQuick::PathLinearGradient::x2 + \qmlproperty real QtQuick::PathLinearGradient::y2 + + These properties define the start and end points between which color + interpolation occurs. By default both the stard and end points are set to + (0, 0). + */ + qreal QQuickPathLinearGradient::x1() const { return m_start.x(); diff --git a/src/quick/items/qquickpathitem_p.h b/src/quick/items/qquickpathitem_p.h index 991ab7a2bf..6d789aadbc 100644 --- a/src/quick/items/qquickpathitem_p.h +++ b/src/quick/items/qquickpathitem_p.h @@ -265,6 +265,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPathItem : public QQuickItem Q_OBJECT Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged) Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) + Q_PROPERTY(bool enableVendorExtensions READ enableVendorExtensions WRITE setEnableVendorExtensions NOTIFY enableVendorExtensionsChanged) Q_PROPERTY(Status status READ status NOTIFY statusChanged) Q_PROPERTY(QQmlListProperty elements READ elements) Q_CLASSINFO("DefaultProperty", "elements") @@ -293,6 +294,9 @@ public: bool asynchronous() const; void setAsynchronous(bool async); + bool enableVendorExtensions() const; + void setEnableVendorExtensions(bool enable); + Status status() const; QQmlListProperty elements(); @@ -313,6 +317,7 @@ protected: Q_SIGNALS: void rendererChanged(); void asynchronousChanged(); + void enableVendorExtensionsChanged(); void statusChanged(); private: diff --git a/src/quick/items/qquickpathitem_p_p.h b/src/quick/items/qquickpathitem_p_p.h index 091a453c0b..c9a2904a25 100644 --- a/src/quick/items/qquickpathitem_p_p.h +++ b/src/quick/items/qquickpathitem_p_p.h @@ -194,6 +194,8 @@ public: QVector paths; QVector sfp; } jsData; + + bool enableVendorExts; }; class QQuickPathItemPathObject : public QObject -- cgit v1.2.3 From 4cddb73882ed950f652fd1e079bc4cf8ccde93d5 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 28 Mar 2017 13:19:29 +0200 Subject: Add a PathItem autotest for the declarative API Change-Id: I276c185c93122e5eb05ef6678ab62fa6928f2523 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitem.cpp | 20 +++++++++++++++++++- src/quick/items/qquickpathitem_p.h | 2 ++ 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp index b14fc1caba..fff666c205 100644 --- a/src/quick/items/qquickpathitem.cpp +++ b/src/quick/items/qquickpathitem.cpp @@ -1155,6 +1155,20 @@ QQuickPathGradient::QQuickPathGradient(QObject *parent) { } +int QQuickPathGradient::countStops(QQmlListProperty *list) +{ + QQuickPathGradient *grad = qobject_cast(list->object); + Q_ASSERT(grad); + return grad->m_stops.count(); +} + +QObject *QQuickPathGradient::atStop(QQmlListProperty *list, int index) +{ + QQuickPathGradient *grad = qobject_cast(list->object); + Q_ASSERT(grad); + return grad->m_stops.at(index); +} + void QQuickPathGradient::appendStop(QQmlListProperty *list, QObject *stop) { QQuickPathGradientStop *sstop = qobject_cast(stop); @@ -1178,7 +1192,11 @@ void QQuickPathGradient::appendStop(QQmlListProperty *list, QObject *st QQmlListProperty QQuickPathGradient::stops() { - return QQmlListProperty(this, nullptr, &QQuickPathGradient::appendStop, nullptr, nullptr, nullptr); + return QQmlListProperty(this, nullptr, + &QQuickPathGradient::appendStop, + &QQuickPathGradient::countStops, + &QQuickPathGradient::atStop, + nullptr); } QGradientStops QQuickPathGradient::sortedGradientStops() const diff --git a/src/quick/items/qquickpathitem_p.h b/src/quick/items/qquickpathitem_p.h index 6d789aadbc..37b23dee6f 100644 --- a/src/quick/items/qquickpathitem_p.h +++ b/src/quick/items/qquickpathitem_p.h @@ -114,6 +114,8 @@ signals: void spreadChanged(); private: + static int countStops(QQmlListProperty *list); + static QObject *atStop(QQmlListProperty *list, int index); static void appendStop(QQmlListProperty *list, QObject *stop); QVector m_stops; -- cgit v1.2.3 From 4f2c5aa8970958b333998ad841d9162a5bfee644 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 29 Mar 2017 13:37:56 +0200 Subject: PathItem/generic: Fix spread modes in gradient material Not including the mode in the comparison makes the scenegraph think that two Pathitems with two linear gradients that only differ in the spread mode can reuse the same material. That would be wrong. Change-Id: I34ad1b74ec8f98ec3a11d6a2565e0a7e5ae3673a Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemgenericrenderer.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp index 7a259617dd..4e8fe55df2 100644 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -744,6 +744,10 @@ int QQuickPathItemLinearGradientMaterial::compare(const QSGMaterial *other) cons const QQuickPathItemGradientCache::GradientDesc *ga = &a->m_fillGradient; const QQuickPathItemGradientCache::GradientDesc *gb = &b->m_fillGradient; + + if (int d = ga->spread - gb->spread) + return d; + if (int d = ga->start.x() - gb->start.x()) return d; if (int d = ga->start.y() - gb->start.y()) -- cgit v1.2.3 From 349d3400c11c0ad1c9aaec01c44b174dbb6ebf9a Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 29 Mar 2017 14:17:51 +0200 Subject: PathItem: add some docs for the JS API Change-Id: I25a1e14e755350350f9a37ab0ac711576c25f535 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitem.cpp | 211 +++++++++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) (limited to 'src') diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp index fff666c205..fae16064e5 100644 --- a/src/quick/items/qquickpathitem.cpp +++ b/src/quick/items/qquickpathitem.cpp @@ -1973,11 +1973,142 @@ void QQuickPathItemPathObject::setV4Engine(QV4::ExecutionEngine *engine) m_v4value = wrapper; } +/*! + \qmltype JSPath + \inqmlmodule QtQuick + \ingroup qtquick-path + \brief Describes a path via a JavaScript API + */ + +/*! + \qmlmethod void QtQuick::JSPath::moveTo(x, y) + + Moves the path's position to the absolute position specified by (\a x, \a y). + */ + +/*! + \qmlmethod void QtQuick::JSPath::lineTo(x, y) + + Defines a straight line to the absolute position specified by (\a x, \a y). + */ + +/*! + \qmlmethod void QtQuick::JSPath::quadTo(cx, cy, x, y) + + Defines a quadratic Bezier curve with a control point (\a cx, \a cy) and an + end point of (\a x, \a y). + */ + +/*! + \qmlmethod void QtQuick::JSPath::cubicTo(c1x, c1y, c2x, c2y, x, y) + + Defines a cubic Bezier curve with two control points (\a c1x, \a c1y) and + (\a c2x, \a c2y), and an end point of (\a x, \a y). + */ + +/*! + \qmlmethod void QtQuick::JSPath::arcTo(radiusX, radiusY, xAxisRotation, x, y, sweepFlag, largeArc) + + Defines an elliptical arc, following the elliptical arc command in SVG. See + \l{https://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands}{the + SVG path specification} for details on the parameters. + */ + +/*! + \qmlmethod void QtQuick::JSPath::clear() + + Clears the path object removing all path elements. This is more lightweight + than creating a new JSPath object. + */ + void QQuickPathItemPathObject::clear() { path = QQuickPathItemPath(); } +/*! + \qmltype StrokeFillParams + \inqmlmodule QtQuick + \ingroup qtquick-path + \brief Describes stroke and fill parameters via a JavaScript API + + The properties of StrokeFillParams objects correspond 1:1 to VisualPath + properties. The possible values for enumerations are the same as well, for + example: + + \code + sfp.strokeStyle = VisualPath.DashLine; + sfp.capStyle = VisualPath.RoundCap; + \endcode + */ + +/*! + \qmlproperty color QtQuick::StrokeFillParams::strokeColor + */ + +/*! + \qmlproperty real QtQuick::StrokeFillParams::strokeWidth + */ + +/*! + \qmlproperty color QtQuick::StrokeFillParams::fillColor + */ + +/*! + \qmlproperty enumeration QtQuick::StrokeFillParams::fillRule + */ + +/*! + \qmlproperty enumeration QtQuick::StrokeFillParams::joinStyle + */ + +/*! + \qmlproperty int QtQuick::StrokeFillParams::miterLimit + */ + +/*! + \qmlproperty enumeration QtQuick::StrokeFillParams::capStyle + */ + +/*! + \qmlproperty enumeration QtQuick::StrokeFillParams::strokeStyle + */ + +/*! + \qmlproperty real QtQuick::StrokeFillParams::dashOffset + */ + +/*! + \qmlproperty list QtQuick::StrokeFillParams::dashPattern + + The dash pattern can be specified using JavaScript arrays. + + \code + sfp.dashPattern = [ 4, 2 ]; + \endcode + */ + +/*! + \qmlproperty object QtQuick::StrokeFillParams::fillGradient + + Sets the fill gradient. The default value is null. Gradients cannot be + created from JavaScript. Instead, reference a PathLinearGradient or other + item by id. + + \code + PathLinearGradient { id: grad; ... } + ... + sfp.fillGradient = grad; + \endcode + */ + +/*! + \qmlmethod void QtQuick::StrokeFillParams::clear() + + Resets all values to their defaults. This is more lightweight than creating + a new StrokeFillParams object. + */ + void QQuickPathItemStrokeFillParamsObject::clear() { sfp = QQuickPathItemStrokeFillParams(); @@ -1996,6 +2127,45 @@ void QQuickPathItemStrokeFillParamsObject::setV4Engine(QV4::ExecutionEngine *eng m_v4value = wrapper; } +/*! + \qmlmethod JSPath QtQuick::PathItem::newPath() + + Creates and returns a new object that describes a path and offers a + JavaScript API. Paired with a stroke-fill parameter object it is + equivalent to a VisualPath. + + The following two snippets are equivalent when it comes to the end result: + + \code + var p = pathItem.newPath(); + var sfp = pathItem.newStrokeFillParams(); + sfp.fillColor = "white"; + sfp.strokeColor = "black"; + sfp.strokeWidth = 0.172; + p.moveTo(-122.304, 84.285); + p.cubicTo(-122.304, 84.285, -122.203, 86.179, -123.027, 86.16); + pathItem.appendVisualPath(p, sfp); + \endcode + + \code + PathItem { + VisualPath { + fillColor: "white" + strokeColor: "black" + strokeWidth: 0.172 + Path { + startX: -122.304; startY: 84.285 + PathCubic { control1X: -122.304; control1Y: 84.285; control2X: -122.203; control2Y: 86.179; x: -123.027; y: 86.16 } + } + } + } + \endcode + + The latter offers a full declarative API, with the possibility to binding + to and animating properties, while the former uses less resources due to + greatly reducing the number of QObject instances created. +*/ + void QQuickPathItem::newPath(QQmlV4Function *args) { QQuickPathItemPathObject *obj = new QQuickPathItemPathObject(this); @@ -2003,6 +2173,14 @@ void QQuickPathItem::newPath(QQmlV4Function *args) args->setReturnValue(obj->v4value()); } +/*! + \qmlmethod StrokeFillParams QtQuick::PathItem::newStrokeFillParams() + + Creates and returns a new object that describes stroke and fill parameters + and offers a JavaScript API. Paired with a path object it is equivalent to + a VisualPath. + */ + void QQuickPathItem::newStrokeFillParams(QQmlV4Function *args) { QQuickPathItemStrokeFillParamsObject *obj = new QQuickPathItemStrokeFillParamsObject(this); @@ -2010,6 +2188,15 @@ void QQuickPathItem::newStrokeFillParams(QQmlV4Function *args) args->setReturnValue(obj->v4value()); } +/*! + \qmlmethod void QtQuick::PathItem::clearVisualPaths() + + Clears the list of visual paths. + + \note This applies only to path and stroke-fill parameter objects registered + via appendVisualPaths(). + */ + void QQuickPathItem::clearVisualPaths(QQmlV4Function *args) { Q_UNUSED(args); @@ -2018,6 +2205,17 @@ void QQuickPathItem::clearVisualPaths(QQmlV4Function *args) d->jsData.sfp.clear(); } +/*! + \qmlmethod void QtQuick::PathItem::commitVisualPaths() + + Updates the PathItem. + + In order to avoid rendering a half-prepared PathItem, calling + PathItem.appendVisualPath() does not trigger any processing. Instead, + applications must call this function when all path and stroke-fill + parameter objects are registered. + */ + void QQuickPathItem::commitVisualPaths(QQmlV4Function *args) { Q_UNUSED(args); @@ -2025,6 +2223,19 @@ void QQuickPathItem::commitVisualPaths(QQmlV4Function *args) d->_q_visualPathChanged(); } +/*! + \qmlmethod void QtQuick::PathItem::appendVisualPath(object path, object strokeFillParams) + + Adds the visual path compoes of \a path and \a strokeFillParams into the + PathItem. + + \note The declarative and imprative (JavaScript) APIs of PathItem use + independent data structures. Calling this function has no effect on the + PathItem.elements property and vice versa. Once this function is called, + the PathItem will only consider the data registered via this function and + will ignore the declarative elements property. + */ + void QQuickPathItem::appendVisualPath(QQmlV4Function *args) { if (args->length() < 2) -- cgit v1.2.3 From e2520ff76be49c5aa917741cc6a380fe1549e47d Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 3 Apr 2017 11:58:46 +0200 Subject: Move PathItem to qt.labs Change-Id: I1cd686cff60bd40fe2cbbc34f917fac7835b6b7d Reviewed-by: Qt CI Bot Reviewed-by: Andy Nichols Reviewed-by: Robin Burchell --- src/imports/imports.pro | 2 + src/imports/pathitem/pathitem.pro | 31 + src/imports/pathitem/plugin.cpp | 74 + src/imports/pathitem/plugins.qmltypes | 312 +++ src/imports/pathitem/qmldir | 4 + src/imports/pathitem/qquicknvprfunctions.cpp | 284 +++ src/imports/pathitem/qquicknvprfunctions_p.h | 406 ++++ src/imports/pathitem/qquicknvprfunctions_p_p.h | 70 + src/imports/pathitem/qquickpathitem.cpp | 2258 ++++++++++++++++++++ src/imports/pathitem/qquickpathitem_p.h | 333 +++ src/imports/pathitem/qquickpathitem_p_p.h | 282 +++ .../pathitem/qquickpathitemgenericrenderer.cpp | 775 +++++++ .../pathitem/qquickpathitemgenericrenderer_p.h | 303 +++ .../pathitem/qquickpathitemnvprrenderer.cpp | 923 ++++++++ .../pathitem/qquickpathitemnvprrenderer_p.h | 237 ++ .../pathitem/qquickpathitemsoftwarerenderer.cpp | 277 +++ .../pathitem/qquickpathitemsoftwarerenderer_p.h | 136 ++ src/quick/items/context2d/qquickcontext2d.cpp | 3 +- src/quick/items/items.pri | 15 +- src/quick/items/qquickitemsmodule.cpp | 6 - src/quick/items/qquickpathitem.cpp | 2258 -------------------- src/quick/items/qquickpathitem_p.h | 335 --- src/quick/items/qquickpathitem_p_p.h | 282 --- src/quick/items/qquickpathitemgenericrenderer.cpp | 775 ------- src/quick/items/qquickpathitemgenericrenderer_p.h | 303 --- src/quick/items/qquickpathitemnvprrenderer.cpp | 923 -------- src/quick/items/qquickpathitemnvprrenderer_p.h | 237 -- src/quick/items/qquickpathitemsoftwarerenderer.cpp | 277 --- src/quick/items/qquickpathitemsoftwarerenderer_p.h | 136 -- src/quick/util/qquicknvprfunctions.cpp | 284 --- src/quick/util/qquicknvprfunctions_p.h | 406 ---- src/quick/util/qquicknvprfunctions_p_p.h | 70 - src/quick/util/qquicksvgparser_p.h | 7 +- src/quick/util/util.pri | 7 - 34 files changed, 6715 insertions(+), 6316 deletions(-) create mode 100644 src/imports/pathitem/pathitem.pro create mode 100644 src/imports/pathitem/plugin.cpp create mode 100644 src/imports/pathitem/plugins.qmltypes create mode 100644 src/imports/pathitem/qmldir create mode 100644 src/imports/pathitem/qquicknvprfunctions.cpp create mode 100644 src/imports/pathitem/qquicknvprfunctions_p.h create mode 100644 src/imports/pathitem/qquicknvprfunctions_p_p.h create mode 100644 src/imports/pathitem/qquickpathitem.cpp create mode 100644 src/imports/pathitem/qquickpathitem_p.h create mode 100644 src/imports/pathitem/qquickpathitem_p_p.h create mode 100644 src/imports/pathitem/qquickpathitemgenericrenderer.cpp create mode 100644 src/imports/pathitem/qquickpathitemgenericrenderer_p.h create mode 100644 src/imports/pathitem/qquickpathitemnvprrenderer.cpp create mode 100644 src/imports/pathitem/qquickpathitemnvprrenderer_p.h create mode 100644 src/imports/pathitem/qquickpathitemsoftwarerenderer.cpp create mode 100644 src/imports/pathitem/qquickpathitemsoftwarerenderer_p.h delete mode 100644 src/quick/items/qquickpathitem.cpp delete mode 100644 src/quick/items/qquickpathitem_p.h delete mode 100644 src/quick/items/qquickpathitem_p_p.h delete mode 100644 src/quick/items/qquickpathitemgenericrenderer.cpp delete mode 100644 src/quick/items/qquickpathitemgenericrenderer_p.h delete mode 100644 src/quick/items/qquickpathitemnvprrenderer.cpp delete mode 100644 src/quick/items/qquickpathitemnvprrenderer_p.h delete mode 100644 src/quick/items/qquickpathitemsoftwarerenderer.cpp delete mode 100644 src/quick/items/qquickpathitemsoftwarerenderer_p.h delete mode 100644 src/quick/util/qquicknvprfunctions.cpp delete mode 100644 src/quick/util/qquicknvprfunctions_p.h delete mode 100644 src/quick/util/qquicknvprfunctions_p_p.h (limited to 'src') diff --git a/src/imports/imports.pro b/src/imports/imports.pro index c03224958c..df0ad01c06 100644 --- a/src/imports/imports.pro +++ b/src/imports/imports.pro @@ -22,6 +22,8 @@ qtHaveModule(quick) { qtConfig(systemsemaphore): SUBDIRS += sharedimage qtConfig(quick-particles): \ SUBDIRS += particles + + SUBDIRS += pathitem } qtHaveModule(xmlpatterns) : SUBDIRS += xmllistmodel diff --git a/src/imports/pathitem/pathitem.pro b/src/imports/pathitem/pathitem.pro new file mode 100644 index 0000000000..d70bb6f203 --- /dev/null +++ b/src/imports/pathitem/pathitem.pro @@ -0,0 +1,31 @@ +CXX_MODULE = qml +TARGET = qmlpathitemplugin +TARGETPATH = Qt/labs/pathitem +IMPORT_VERSION = 1.0 + +QT = core gui qml quick quick-private + +HEADERS += \ + qquickpathitem_p.h \ + qquickpathitem_p_p.h \ + qquickpathitemgenericrenderer_p.h \ + qquickpathitemsoftwarerenderer_p.h + +SOURCES += \ + plugin.cpp \ + qquickpathitem.cpp \ + qquickpathitemgenericrenderer.cpp \ + qquickpathitemsoftwarerenderer.cpp + +qtConfig(opengl) { + HEADERS += \ + qquicknvprfunctions_p.h \ + qquicknvprfunctions_p_p.h \ + qquickpathitemnvprrenderer_p.h + + SOURCES += \ + qquicknvprfunctions.cpp \ + qquickpathitemnvprrenderer.cpp +} + +load(qml_plugin) diff --git a/src/imports/pathitem/plugin.cpp b/src/imports/pathitem/plugin.cpp new file mode 100644 index 0000000000..6b43e8f398 --- /dev/null +++ b/src/imports/pathitem/plugin.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "qquickpathitem_p.h" + +static void initResources() +{ +#ifdef QT_STATIC + Q_INIT_RESOURCE(qmake_Qt_labs_pathitem); +#endif +} + +QT_BEGIN_NAMESPACE + +class QmlPathItemPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + QmlPathItemPlugin(QObject *parent = 0) : QQmlExtensionPlugin(parent) { initResources(); } + void registerTypes(const char *uri) Q_DECL_OVERRIDE + { + Q_ASSERT(QByteArray(uri) == QByteArray("Qt.labs.pathitem")); + qmlRegisterType(uri, 1, 0, "PathItem"); + qmlRegisterType(uri, 1, 0, "VisualPath"); + qmlRegisterType(uri, 1, 0, "PathGradientStop"); + qmlRegisterUncreatableType(uri, 1, 0, "PathGradient", QQuickPathGradient::tr("PathGradient is an abstract base class")); + qmlRegisterType(uri, 1, 0, "PathLinearGradient"); + } +}; + +QT_END_NAMESPACE + +#include "plugin.moc" diff --git a/src/imports/pathitem/plugins.qmltypes b/src/imports/pathitem/plugins.qmltypes new file mode 100644 index 0000000000..03f26e243c --- /dev/null +++ b/src/imports/pathitem/plugins.qmltypes @@ -0,0 +1,312 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by: +// 'qmlplugindump -nonrelocatable -noforceqtquick Qt.labs.pathitem 1.0' + +Module { + dependencies: [] + Component { + name: "QQuickItem" + defaultProperty: "data" + prototype: "QObject" + Enum { + name: "TransformOrigin" + values: { + "TopLeft": 0, + "Top": 1, + "TopRight": 2, + "Left": 3, + "Center": 4, + "Right": 5, + "BottomLeft": 6, + "Bottom": 7, + "BottomRight": 8 + } + } + Property { name: "parent"; type: "QQuickItem"; isPointer: true } + Property { name: "data"; type: "QObject"; isList: true; isReadonly: true } + Property { name: "resources"; type: "QObject"; isList: true; isReadonly: true } + Property { name: "children"; type: "QQuickItem"; isList: true; isReadonly: true } + Property { name: "x"; type: "double" } + Property { name: "y"; type: "double" } + Property { name: "z"; type: "double" } + Property { name: "width"; type: "double" } + Property { name: "height"; type: "double" } + Property { name: "opacity"; type: "double" } + Property { name: "enabled"; type: "bool" } + Property { name: "visible"; type: "bool" } + Property { name: "visibleChildren"; type: "QQuickItem"; isList: true; isReadonly: true } + Property { name: "states"; type: "QQuickState"; isList: true; isReadonly: true } + Property { name: "transitions"; type: "QQuickTransition"; isList: true; isReadonly: true } + Property { name: "state"; type: "string" } + Property { name: "childrenRect"; type: "QRectF"; isReadonly: true } + Property { name: "anchors"; type: "QQuickAnchors"; isReadonly: true; isPointer: true } + Property { name: "left"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "right"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "horizontalCenter"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "top"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "bottom"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "verticalCenter"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "baseline"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "baselineOffset"; type: "double" } + Property { name: "clip"; type: "bool" } + Property { name: "focus"; type: "bool" } + Property { name: "activeFocus"; type: "bool"; isReadonly: true } + Property { name: "activeFocusOnTab"; revision: 1; type: "bool" } + Property { name: "rotation"; type: "double" } + Property { name: "scale"; type: "double" } + Property { name: "transformOrigin"; type: "TransformOrigin" } + Property { name: "transformOriginPoint"; type: "QPointF"; isReadonly: true } + Property { name: "transform"; type: "QQuickTransform"; isList: true; isReadonly: true } + Property { name: "smooth"; type: "bool" } + Property { name: "antialiasing"; type: "bool" } + Property { name: "implicitWidth"; type: "double" } + Property { name: "implicitHeight"; type: "double" } + Property { name: "layer"; type: "QQuickItemLayer"; isReadonly: true; isPointer: true } + Signal { + name: "childrenRectChanged" + Parameter { type: "QRectF" } + } + Signal { + name: "baselineOffsetChanged" + Parameter { type: "double" } + } + Signal { + name: "stateChanged" + Parameter { type: "string" } + } + Signal { + name: "focusChanged" + Parameter { type: "bool" } + } + Signal { + name: "activeFocusChanged" + Parameter { type: "bool" } + } + Signal { + name: "activeFocusOnTabChanged" + revision: 1 + Parameter { type: "bool" } + } + Signal { + name: "parentChanged" + Parameter { type: "QQuickItem"; isPointer: true } + } + Signal { + name: "transformOriginChanged" + Parameter { type: "TransformOrigin" } + } + Signal { + name: "smoothChanged" + Parameter { type: "bool" } + } + Signal { + name: "antialiasingChanged" + Parameter { type: "bool" } + } + Signal { + name: "clipChanged" + Parameter { type: "bool" } + } + Signal { + name: "windowChanged" + revision: 1 + Parameter { name: "window"; type: "QQuickWindow"; isPointer: true } + } + Method { name: "update" } + Method { + name: "grabToImage" + revision: 2 + type: "bool" + Parameter { name: "callback"; type: "QJSValue" } + Parameter { name: "targetSize"; type: "QSize" } + } + Method { + name: "grabToImage" + revision: 2 + type: "bool" + Parameter { name: "callback"; type: "QJSValue" } + } + Method { + name: "contains" + type: "bool" + Parameter { name: "point"; type: "QPointF" } + } + Method { + name: "mapFromItem" + Parameter { type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "mapToItem" + Parameter { type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "mapFromGlobal" + revision: 7 + Parameter { type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "mapToGlobal" + revision: 7 + Parameter { type: "QQmlV4Function"; isPointer: true } + } + Method { name: "forceActiveFocus" } + Method { + name: "forceActiveFocus" + Parameter { name: "reason"; type: "Qt::FocusReason" } + } + Method { + name: "nextItemInFocusChain" + revision: 1 + type: "QQuickItem*" + Parameter { name: "forward"; type: "bool" } + } + Method { name: "nextItemInFocusChain"; revision: 1; type: "QQuickItem*" } + Method { + name: "childAt" + type: "QQuickItem*" + Parameter { name: "x"; type: "double" } + Parameter { name: "y"; type: "double" } + } + } + Component { + name: "QQuickPathGradient" + defaultProperty: "stops" + prototype: "QObject" + exports: ["Qt.labs.pathitem/PathGradient 1.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + Enum { + name: "SpreadMode" + values: { + "PadSpread": 0, + "RepeatSpread": 1, + "ReflectSpread": 2 + } + } + Property { name: "stops"; type: "QObject"; isList: true; isReadonly: true } + Property { name: "spread"; type: "SpreadMode" } + Signal { name: "updated" } + } + Component { + name: "QQuickPathGradientStop" + prototype: "QObject" + exports: ["Qt.labs.pathitem/PathGradientStop 1.0"] + exportMetaObjectRevisions: [0] + Property { name: "position"; type: "double" } + Property { name: "color"; type: "QColor" } + } + Component { + name: "QQuickPathItem" + defaultProperty: "elements" + prototype: "QQuickItem" + exports: ["Qt.labs.pathitem/PathItem 1.0"] + exportMetaObjectRevisions: [0] + Enum { + name: "RendererType" + values: { + "UnknownRenderer": 0, + "GeometryRenderer": 1, + "NvprRenderer": 2, + "SoftwareRenderer": 3 + } + } + Enum { + name: "Status" + values: { + "Null": 0, + "Ready": 1, + "Processing": 2 + } + } + Property { name: "renderer"; type: "RendererType"; isReadonly: true } + Property { name: "asynchronous"; type: "bool" } + Property { name: "enableVendorExtensions"; type: "bool" } + Property { name: "status"; type: "Status"; isReadonly: true } + Property { name: "elements"; type: "QQuickVisualPath"; isList: true; isReadonly: true } + Method { + name: "newPath" + Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "newStrokeFillParams" + Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "clearVisualPaths" + Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "commitVisualPaths" + Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "appendVisualPath" + Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true } + } + } + Component { + name: "QQuickPathLinearGradient" + defaultProperty: "stops" + prototype: "QQuickPathGradient" + exports: ["Qt.labs.pathitem/PathLinearGradient 1.0"] + exportMetaObjectRevisions: [0] + Property { name: "x1"; type: "double" } + Property { name: "y1"; type: "double" } + Property { name: "x2"; type: "double" } + Property { name: "y2"; type: "double" } + } + Component { + name: "QQuickVisualPath" + defaultProperty: "path" + prototype: "QObject" + exports: ["Qt.labs.pathitem/VisualPath 1.0"] + exportMetaObjectRevisions: [0] + Enum { + name: "FillRule" + values: { + "OddEvenFill": 0, + "WindingFill": 1 + } + } + Enum { + name: "JoinStyle" + values: { + "MiterJoin": 0, + "BevelJoin": 64, + "RoundJoin": 128 + } + } + Enum { + name: "CapStyle" + values: { + "FlatCap": 0, + "SquareCap": 16, + "RoundCap": 32 + } + } + Enum { + name: "StrokeStyle" + values: { + "SolidLine": 1, + "DashLine": 2 + } + } + Property { name: "path"; type: "QQuickPath"; isPointer: true } + Property { name: "strokeColor"; type: "QColor" } + Property { name: "strokeWidth"; type: "double" } + Property { name: "fillColor"; type: "QColor" } + Property { name: "fillRule"; type: "FillRule" } + Property { name: "joinStyle"; type: "JoinStyle" } + Property { name: "miterLimit"; type: "int" } + Property { name: "capStyle"; type: "CapStyle" } + Property { name: "strokeStyle"; type: "StrokeStyle" } + Property { name: "dashOffset"; type: "double" } + Property { name: "dashPattern"; type: "QVector" } + Property { name: "fillGradient"; type: "QQuickPathGradient"; isPointer: true } + Signal { name: "changed" } + } +} diff --git a/src/imports/pathitem/qmldir b/src/imports/pathitem/qmldir new file mode 100644 index 0000000000..277b8a199b --- /dev/null +++ b/src/imports/pathitem/qmldir @@ -0,0 +1,4 @@ +module Qt.labs.pathitem +plugin qmlpathitemplugin +classname QmlPathItemPlugin +typeinfo plugins.qmltypes diff --git a/src/imports/pathitem/qquicknvprfunctions.cpp b/src/imports/pathitem/qquicknvprfunctions.cpp new file mode 100644 index 0000000000..40eb2bb932 --- /dev/null +++ b/src/imports/pathitem/qquicknvprfunctions.cpp @@ -0,0 +1,284 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicknvprfunctions_p.h" + +#ifndef QT_NO_OPENGL + +#include +#include +#include +#include "qquicknvprfunctions_p_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QQuickNvprFunctions + + \brief Function resolvers and other helpers for GL_NV_path_rendering + for both desktop (GL 4.3+) and mobile/embedded (GLES 3.1+) in a manner + that does not distract builds that do not have NVPR support either at + compile or run time. + + \internal + */ + +QQuickNvprFunctions::QQuickNvprFunctions() + : d(new QQuickNvprFunctionsPrivate(this)) +{ +} + +QQuickNvprFunctions::~QQuickNvprFunctions() +{ + delete d; +} + +/*! + \return a recommended QSurfaceFormat suitable for GL_NV_path_rendering on top + of OpenGL 4.3 or OpenGL ES 3.1. + */ +QSurfaceFormat QQuickNvprFunctions::format() +{ + QSurfaceFormat fmt; + fmt.setDepthBufferSize(24); + fmt.setStencilBufferSize(8); + if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { + fmt.setVersion(4, 3); + fmt.setProfile(QSurfaceFormat::CompatibilityProfile); + } else if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { + fmt.setVersion(3, 1); + } + return fmt; +} + +/*! + \return true if GL_NV_path_rendering is supported with the current OpenGL + context. + + When there is no current context, a temporary dummy one will be created and + made current. + */ +bool QQuickNvprFunctions::isSupported() +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + QScopedPointer tempContext; + QScopedPointer tempSurface; + if (!ctx) { + tempContext.reset(new QOpenGLContext); + if (!tempContext->create()) + return false; + ctx = tempContext.data(); + tempSurface.reset(new QOffscreenSurface); + tempSurface->setFormat(ctx->format()); + tempSurface->create(); + if (!ctx->makeCurrent(tempSurface.data())) + return false; + } + + if (!ctx->hasExtension(QByteArrayLiteral("GL_NV_path_rendering"))) + return false; + + // Do not check for DSA as the string may not be exposed on ES + // drivers, yet the functions we need are resolvable. +#if 0 + if (!ctx->hasExtension(QByteArrayLiteral("GL_EXT_direct_state_access"))) { + qWarning("QtQuickPath/NVPR: GL_EXT_direct_state_access not supported"); + return false; + } +#endif + + return true; +} + +/*! + Initializes using the current OpenGL context. + + \return true when GL_NV_path_rendering is supported and initialization was + successful. + */ +bool QQuickNvprFunctions::create() +{ + return isSupported() && d->resolve(); +} + +/*! + Creates a program pipeline consisting of a separable fragment shader program. + + This is essential for using NVPR with OpenGL ES 3.1+ since normal, + GLES2-style programs would not work without a vertex shader. + + \note \a fragmentShaderSource should be a \c{version 310 es} shader since + this works both on desktop and embedded NVIDIA drivers, thus avoiding the + need to fight GLSL and GLSL ES differences. + + The pipeline object is stored into \a pipeline, the fragment shader program + into \a program. + + Use QOpenGLExtraFunctions to set uniforms, bind the pipeline, etc. + + \return \c false on failure in which case the error log is printed on the + debug output. \c true on success. + */ +bool QQuickNvprFunctions::createFragmentOnlyPipeline(const char *fragmentShaderSource, GLuint *pipeline, GLuint *program) +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (!ctx) + return false; + + QOpenGLExtraFunctions *f = ctx->extraFunctions(); + *program = f->glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &fragmentShaderSource); + GLint status = 0; + f->glGetProgramiv(*program, GL_LINK_STATUS, &status); + if (!status) { + GLint len = 0; + f->glGetProgramiv(*program, GL_INFO_LOG_LENGTH, &len); + if (len) { + QByteArray s; + s.resize(len); + f->glGetProgramInfoLog(*program, s.count(), nullptr, s.data()); + qWarning("Failed to create separable shader program:\n%s", s.constData()); + } + return false; + } + + f->glGenProgramPipelines(1, pipeline); + f->glUseProgramStages(*pipeline, GL_FRAGMENT_SHADER_BIT, *program); + f->glActiveShaderProgram(*pipeline, *program); + + f->glValidateProgramPipeline(*pipeline); + status = 0; + f->glGetProgramPipelineiv(*pipeline, GL_VALIDATE_STATUS, &status); + if (!status) { + GLint len = 0; + f->glGetProgramPipelineiv(*pipeline, GL_INFO_LOG_LENGTH, &len); + if (len) { + QByteArray s; + s.resize(len); + f->glGetProgramPipelineInfoLog(*pipeline, s.count(), nullptr, s.data()); + qWarning("Program pipeline validation failed:\n%s", s.constData()); + } + return false; + } + + return true; +} + +#define PROC(type, name) reinterpret_cast(ctx->getProcAddress(#name)) + +bool QQuickNvprFunctionsPrivate::resolve() +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + + q->genPaths = PROC(PFNGLGENPATHSNVPROC, glGenPathsNV); + q->deletePaths = PROC(PFNGLDELETEPATHSNVPROC, glDeletePathsNV); + q->isPath = PROC(PFNGLISPATHNVPROC, glIsPathNV); + q->pathCommands = PROC(PFNGLPATHCOMMANDSNVPROC, glPathCommandsNV); + q->pathCoords = PROC(PFNGLPATHCOORDSNVPROC, glPathCoordsNV); + q->pathSubCommands = PROC(PFNGLPATHSUBCOMMANDSNVPROC, glPathSubCommandsNV); + q->pathSubCoords = PROC(PFNGLPATHSUBCOORDSNVPROC, glPathSubCoordsNV); + q->pathString = PROC(PFNGLPATHSTRINGNVPROC, glPathStringNV); + q->pathGlyphs = PROC(PFNGLPATHGLYPHSNVPROC, glPathGlyphsNV); + q->pathGlyphRange = PROC(PFNGLPATHGLYPHRANGENVPROC, glPathGlyphRangeNV); + q->weightPaths = PROC(PFNGLWEIGHTPATHSNVPROC, glWeightPathsNV); + q->copyPath = PROC(PFNGLCOPYPATHNVPROC, glCopyPathNV); + q->interpolatePaths = PROC(PFNGLINTERPOLATEPATHSNVPROC, glInterpolatePathsNV); + q->transformPath = PROC(PFNGLTRANSFORMPATHNVPROC, glTransformPathNV); + q->pathParameteriv = PROC(PFNGLPATHPARAMETERIVNVPROC, glPathParameterivNV); + q->pathParameteri = PROC(PFNGLPATHPARAMETERINVPROC, glPathParameteriNV); + q->pathParameterfv = PROC(PFNGLPATHPARAMETERFVNVPROC, glPathParameterfvNV); + q->pathParameterf = PROC(PFNGLPATHPARAMETERFNVPROC, glPathParameterfNV); + q->pathDashArray = PROC(PFNGLPATHDASHARRAYNVPROC, glPathDashArrayNV); + q->pathStencilFunc = PROC(PFNGLPATHSTENCILFUNCNVPROC, glPathStencilFuncNV); + q->pathStencilDepthOffset = PROC(PFNGLPATHSTENCILDEPTHOFFSETNVPROC, glPathStencilDepthOffsetNV); + q->stencilFillPath = PROC(PFNGLSTENCILFILLPATHNVPROC, glStencilFillPathNV); + q->stencilStrokePath = PROC(PFNGLSTENCILSTROKEPATHNVPROC, glStencilStrokePathNV); + q->stencilFillPathInstanced = PROC(PFNGLSTENCILFILLPATHINSTANCEDNVPROC, glStencilFillPathInstancedNV); + q->stencilStrokePathInstanced = PROC(PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC, glStencilStrokePathInstancedNV); + q->pathCoverDepthFunc = PROC(PFNGLPATHCOVERDEPTHFUNCNVPROC, glPathCoverDepthFuncNV); + q->pathColorGen = PROC(PFNGLPATHCOLORGENNVPROC, glPathColorGenNV); + q->pathTexGen = PROC(PFNGLPATHTEXGENNVPROC, glPathTexGenNV); + q->pathFogGen = PROC(PFNGLPATHFOGGENNVPROC, glPathFogGenNV); + q->coverFillPath = PROC(PFNGLCOVERFILLPATHNVPROC, glCoverFillPathNV); + q->coverStrokePath = PROC(PFNGLCOVERSTROKEPATHNVPROC, glCoverStrokePathNV); + q->coverFillPathInstanced = PROC(PFNGLCOVERFILLPATHINSTANCEDNVPROC, glCoverFillPathInstancedNV); + q->coverStrokePathInstanced = PROC(PFNGLCOVERSTROKEPATHINSTANCEDNVPROC, glCoverStrokePathInstancedNV); + q->getPathParameteriv = PROC(PFNGLGETPATHPARAMETERIVNVPROC, glGetPathParameterivNV); + q->getPathParameterfv = PROC(PFNGLGETPATHPARAMETERFVNVPROC, glGetPathParameterfvNV); + q->getPathCommands = PROC(PFNGLGETPATHCOMMANDSNVPROC, glGetPathCommandsNV); + q->getPathCoords = PROC(PFNGLGETPATHCOORDSNVPROC, glGetPathCoordsNV); + q->getPathDashArray = PROC(PFNGLGETPATHDASHARRAYNVPROC, glGetPathDashArrayNV); + q->getPathMetrics = PROC(PFNGLGETPATHMETRICSNVPROC, glGetPathMetricsNV); + q->getPathMetricRange = PROC(PFNGLGETPATHMETRICRANGENVPROC, glGetPathMetricRangeNV); + q->getPathSpacing = PROC(PFNGLGETPATHSPACINGNVPROC, glGetPathSpacingNV); + q->getPathColorgeniv = PROC(PFNGLGETPATHCOLORGENIVNVPROC, glGetPathColorGenivNV); + q->getPathColorgenfv = PROC(PFNGLGETPATHCOLORGENFVNVPROC, glGetPathColorGenfvNV); + q->getPathTexGeniv = PROC(PFNGLGETPATHTEXGENIVNVPROC, glGetPathTexGenivNV); + q->getPathTexGenfv = PROC(PFNGLGETPATHTEXGENFVNVPROC, glGetPathTexGenfvNV); + q->isPointInFillPath = PROC(PFNGLISPOINTINFILLPATHNVPROC, glIsPointInFillPathNV); + q->isPointInStrokePath = PROC(PFNGLISPOINTINSTROKEPATHNVPROC, glIsPointInStrokePathNV); + q->getPathLength = PROC(PFNGLGETPATHLENGTHNVPROC, glGetPathLengthNV); + q->getPointAlongPath = PROC(PFNGLPOINTALONGPATHNVPROC, glPointAlongPathNV); + q->matrixLoad3x2f = PROC(PFNGLMATRIXLOAD3X2FNVPROC, glMatrixLoad3x2fNV); + q->matrixLoad3x3f = PROC(PFNGLMATRIXLOAD3X3FNVPROC, glMatrixLoad3x3fNV); + q->matrixLoadTranspose3x3f = PROC(PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC, glMatrixLoadTranspose3x3fNV); + q->matrixMult3x2f = PROC(PFNGLMATRIXMULT3X2FNVPROC, glMatrixMult3x2fNV); + q->matrixMult3x3f = PROC(PFNGLMATRIXMULT3X3FNVPROC, glMatrixMult3x3fNV); + q->matrixMultTranspose3x3f = PROC(PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC, glMatrixMultTranspose3x3fNV); + q->stencilThenCoverFillPath = PROC(PFNGLSTENCILTHENCOVERFILLPATHNVPROC, glStencilThenCoverFillPathNV); + q->stencilThenCoverStrokePath = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC, glStencilThenCoverStrokePathNV); + q->stencilThenCoverFillPathInstanced = PROC(PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC, glStencilThenCoverFillPathInstancedNV); + q->stencilThenCoverStrokePathInstanced = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC, glStencilThenCoverStrokePathInstancedNV); + q->pathGlyphIndexRange = PROC(PFNGLPATHGLYPHINDEXRANGENVPROC, glPathGlyphIndexRangeNV); + q->pathGlyphIndexArray = PROC(PFNGLPATHGLYPHINDEXARRAYNVPROC, glPathGlyphIndexArrayNV); + q->pathMemoryGlyphIndexArray = PROC(PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC, glPathMemoryGlyphIndexArrayNV); + q->programPathFragmentInputGen = PROC(PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC, glProgramPathFragmentInputGenNV); + q->getProgramResourcefv = PROC(PFNGLGETPROGRAMRESOURCEFVNVPROC, glGetProgramResourcefvNV); + + q->matrixLoadf = PROC(PFNGLMATRIXLOADFEXTPROC, glMatrixLoadfEXT); + q->matrixLoadIdentity = PROC(PFNGLMATRIXLOADIDENTITYEXTPROC, glMatrixLoadIdentityEXT); + + return q->genPaths != nullptr // base path rendering ext + && q->programPathFragmentInputGen != nullptr // updated path rendering ext + && q->matrixLoadf != nullptr // direct state access ext + && q->matrixLoadIdentity != nullptr; +} + +QT_END_NAMESPACE + +#endif // QT_NO_OPENGL diff --git a/src/imports/pathitem/qquicknvprfunctions_p.h b/src/imports/pathitem/qquicknvprfunctions_p.h new file mode 100644 index 0000000000..7900388305 --- /dev/null +++ b/src/imports/pathitem/qquicknvprfunctions_p.h @@ -0,0 +1,406 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKNVPRFUNCTIONS_P_H +#define QQUICKNVPRFUNCTIONS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#ifndef QT_NO_OPENGL + +QT_BEGIN_NAMESPACE + +#ifndef GL_NV_path_rendering +#define GL_PATH_FORMAT_SVG_NV 0x9070 +#define GL_PATH_FORMAT_PS_NV 0x9071 +#define GL_STANDARD_FONT_NAME_NV 0x9072 +#define GL_SYSTEM_FONT_NAME_NV 0x9073 +#define GL_FILE_NAME_NV 0x9074 +#define GL_PATH_STROKE_WIDTH_NV 0x9075 +#define GL_PATH_END_CAPS_NV 0x9076 +#define GL_PATH_INITIAL_END_CAP_NV 0x9077 +#define GL_PATH_TERMINAL_END_CAP_NV 0x9078 +#define GL_PATH_JOIN_STYLE_NV 0x9079 +#define GL_PATH_MITER_LIMIT_NV 0x907A +#define GL_PATH_DASH_CAPS_NV 0x907B +#define GL_PATH_INITIAL_DASH_CAP_NV 0x907C +#define GL_PATH_TERMINAL_DASH_CAP_NV 0x907D +#define GL_PATH_DASH_OFFSET_NV 0x907E +#define GL_PATH_CLIENT_LENGTH_NV 0x907F +#define GL_PATH_FILL_MODE_NV 0x9080 +#define GL_PATH_FILL_MASK_NV 0x9081 +#define GL_PATH_FILL_COVER_MODE_NV 0x9082 +#define GL_PATH_STROKE_COVER_MODE_NV 0x9083 +#define GL_PATH_STROKE_MASK_NV 0x9084 +#define GL_COUNT_UP_NV 0x9088 +#define GL_COUNT_DOWN_NV 0x9089 +#define GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A +#define GL_CONVEX_HULL_NV 0x908B +#define GL_BOUNDING_BOX_NV 0x908D +#define GL_TRANSLATE_X_NV 0x908E +#define GL_TRANSLATE_Y_NV 0x908F +#define GL_TRANSLATE_2D_NV 0x9090 +#define GL_TRANSLATE_3D_NV 0x9091 +#define GL_AFFINE_2D_NV 0x9092 +#define GL_AFFINE_3D_NV 0x9094 +#define GL_TRANSPOSE_AFFINE_2D_NV 0x9096 +#define GL_TRANSPOSE_AFFINE_3D_NV 0x9098 +#define GL_UTF8_NV 0x909A +#define GL_UTF16_NV 0x909B +#define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C +#define GL_PATH_COMMAND_COUNT_NV 0x909D +#define GL_PATH_COORD_COUNT_NV 0x909E +#define GL_PATH_DASH_ARRAY_COUNT_NV 0x909F +#define GL_PATH_COMPUTED_LENGTH_NV 0x90A0 +#define GL_PATH_FILL_BOUNDING_BOX_NV 0x90A1 +#define GL_PATH_STROKE_BOUNDING_BOX_NV 0x90A2 +#define GL_SQUARE_NV 0x90A3 +#define GL_ROUND_NV 0x90A4 +#define GL_TRIANGULAR_NV 0x90A5 +#define GL_BEVEL_NV 0x90A6 +#define GL_MITER_REVERT_NV 0x90A7 +#define GL_MITER_TRUNCATE_NV 0x90A8 +#define GL_SKIP_MISSING_GLYPH_NV 0x90A9 +#define GL_USE_MISSING_GLYPH_NV 0x90AA +#define GL_PATH_ERROR_POSITION_NV 0x90AB +#define GL_PATH_FOG_GEN_MODE_NV 0x90AC +#define GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD +#define GL_ADJACENT_PAIRS_NV 0x90AE +#define GL_FIRST_TO_REST_NV 0x90AF +#define GL_PATH_GEN_MODE_NV 0x90B0 +#define GL_PATH_GEN_COEFF_NV 0x90B1 +#define GL_PATH_GEN_COLOR_FORMAT_NV 0x90B2 +#define GL_PATH_GEN_COMPONENTS_NV 0x90B3 +#define GL_PATH_STENCIL_FUNC_NV 0x90B7 +#define GL_PATH_STENCIL_REF_NV 0x90B8 +#define GL_PATH_STENCIL_VALUE_MASK_NV 0x90B9 +#define GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD +#define GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE +#define GL_PATH_COVER_DEPTH_FUNC_NV 0x90BF +#define GL_PATH_DASH_OFFSET_RESET_NV 0x90B4 +#define GL_MOVE_TO_RESETS_NV 0x90B5 +#define GL_MOVE_TO_CONTINUES_NV 0x90B6 +#define GL_CLOSE_PATH_NV 0x00 +#define GL_MOVE_TO_NV 0x02 +#define GL_RELATIVE_MOVE_TO_NV 0x03 +#define GL_LINE_TO_NV 0x04 +#define GL_RELATIVE_LINE_TO_NV 0x05 +#define GL_HORIZONTAL_LINE_TO_NV 0x06 +#define GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07 +#define GL_VERTICAL_LINE_TO_NV 0x08 +#define GL_RELATIVE_VERTICAL_LINE_TO_NV 0x09 +#define GL_QUADRATIC_CURVE_TO_NV 0x0A +#define GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B +#define GL_CUBIC_CURVE_TO_NV 0x0C +#define GL_RELATIVE_CUBIC_CURVE_TO_NV 0x0D +#define GL_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E +#define GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F +#define GL_SMOOTH_CUBIC_CURVE_TO_NV 0x10 +#define GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11 +#define GL_SMALL_CCW_ARC_TO_NV 0x12 +#define GL_RELATIVE_SMALL_CCW_ARC_TO_NV 0x13 +#define GL_SMALL_CW_ARC_TO_NV 0x14 +#define GL_RELATIVE_SMALL_CW_ARC_TO_NV 0x15 +#define GL_LARGE_CCW_ARC_TO_NV 0x16 +#define GL_RELATIVE_LARGE_CCW_ARC_TO_NV 0x17 +#define GL_LARGE_CW_ARC_TO_NV 0x18 +#define GL_RELATIVE_LARGE_CW_ARC_TO_NV 0x19 +#define GL_RESTART_PATH_NV 0xF0 +#define GL_DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2 +#define GL_DUP_LAST_CUBIC_CURVE_TO_NV 0xF4 +#define GL_RECT_NV 0xF6 +#define GL_CIRCULAR_CCW_ARC_TO_NV 0xF8 +#define GL_CIRCULAR_CW_ARC_TO_NV 0xFA +#define GL_CIRCULAR_TANGENT_ARC_TO_NV 0xFC +#define GL_ARC_TO_NV 0xFE +#define GL_RELATIVE_ARC_TO_NV 0xFF +#define GL_BOLD_BIT_NV 0x01 +#define GL_ITALIC_BIT_NV 0x02 +#define GL_GLYPH_WIDTH_BIT_NV 0x01 +#define GL_GLYPH_HEIGHT_BIT_NV 0x02 +#define GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04 +#define GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08 +#define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10 +#define GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20 +#define GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40 +#define GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80 +#define GL_GLYPH_HAS_KERNING_BIT_NV 0x100 +#define GL_FONT_X_MIN_BOUNDS_BIT_NV 0x00010000 +#define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000 +#define GL_FONT_X_MAX_BOUNDS_BIT_NV 0x00040000 +#define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000 +#define GL_FONT_UNITS_PER_EM_BIT_NV 0x00100000 +#define GL_FONT_ASCENDER_BIT_NV 0x00200000 +#define GL_FONT_DESCENDER_BIT_NV 0x00400000 +#define GL_FONT_HEIGHT_BIT_NV 0x00800000 +#define GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000 +#define GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000 +#define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000 +#define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000 +#define GL_FONT_HAS_KERNING_BIT_NV 0x10000000 +#define GL_PRIMARY_COLOR_NV 0x852C +#define GL_SECONDARY_COLOR_NV 0x852D +#define GL_ROUNDED_RECT_NV 0xE8 +#define GL_RELATIVE_ROUNDED_RECT_NV 0xE9 +#define GL_ROUNDED_RECT2_NV 0xEA +#define GL_RELATIVE_ROUNDED_RECT2_NV 0xEB +#define GL_ROUNDED_RECT4_NV 0xEC +#define GL_RELATIVE_ROUNDED_RECT4_NV 0xED +#define GL_ROUNDED_RECT8_NV 0xEE +#define GL_RELATIVE_ROUNDED_RECT8_NV 0xEF +#define GL_RELATIVE_RECT_NV 0xF7 +#define GL_FONT_GLYPHS_AVAILABLE_NV 0x9368 +#define GL_FONT_TARGET_UNAVAILABLE_NV 0x9369 +#define GL_FONT_UNAVAILABLE_NV 0x936A +#define GL_FONT_UNINTELLIGIBLE_NV 0x936B +#define GL_CONIC_CURVE_TO_NV 0x1A +#define GL_RELATIVE_CONIC_CURVE_TO_NV 0x1B +#define GL_FONT_NUM_GLYPH_INDICES_BIT_NV 0x20000000 +#define GL_STANDARD_FONT_FORMAT_NV 0x936C +#define GL_2_BYTES_NV 0x1407 +#define GL_3_BYTES_NV 0x1408 +#define GL_4_BYTES_NV 0x1409 +#define GL_EYE_LINEAR_NV 0x2400 +#define GL_OBJECT_LINEAR_NV 0x2401 +#define GL_CONSTANT_NV 0x8576 +#define GL_PATH_PROJECTION_NV 0x1701 +#define GL_PATH_MODELVIEW_NV 0x1700 +#define GL_PATH_MODELVIEW_STACK_DEPTH_NV 0x0BA3 +#define GL_PATH_MODELVIEW_MATRIX_NV 0x0BA6 +#define GL_PATH_MAX_MODELVIEW_STACK_DEPTH_NV 0x0D36 +#define GL_PATH_TRANSPOSE_MODELVIEW_MATRIX_NV 0x84E3 +#define GL_PATH_PROJECTION_STACK_DEPTH_NV 0x0BA4 +#define GL_PATH_PROJECTION_MATRIX_NV 0x0BA7 +#define GL_PATH_MAX_PROJECTION_STACK_DEPTH_NV 0x0D38 +#define GL_PATH_TRANSPOSE_PROJECTION_MATRIX_NV 0x84E4 +#define GL_FRAGMENT_INPUT_NV 0x936D + +typedef GLuint (QOPENGLF_APIENTRYP PFNGLGENPATHSNVPROC) (GLsizei range); +typedef void (QOPENGLF_APIENTRYP PFNGLDELETEPATHSNVPROC) (GLuint path, GLsizei range); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPATHNVPROC) (GLuint path); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOMMANDSNVPROC) (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOORDSNVPROC) (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSUBCOMMANDSNVPROC) (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSUBCOORDSNVPROC) (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTRINGNVPROC) (GLuint path, GLenum format, GLsizei length, const void *pathString); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHGLYPHSNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHGLYPHRANGENVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (QOPENGLF_APIENTRYP PFNGLWEIGHTPATHSNVPROC) (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); +typedef void (QOPENGLF_APIENTRYP PFNGLCOPYPATHNVPROC) (GLuint resultPath, GLuint srcPath); +typedef void (QOPENGLF_APIENTRYP PFNGLINTERPOLATEPATHSNVPROC) (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); +typedef void (QOPENGLF_APIENTRYP PFNGLTRANSFORMPATHNVPROC) (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, const GLint *value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERINVPROC) (GLuint path, GLenum pname, GLint value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, const GLfloat *value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERFNVPROC) (GLuint path, GLenum pname, GLfloat value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHDASHARRAYNVPROC) (GLuint path, GLsizei dashCount, const GLfloat *dashArray); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTENCILFUNCNVPROC) (GLenum func, GLint ref, GLuint mask); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTENCILDEPTHOFFSETNVPROC) (GLfloat factor, GLfloat units); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOVERDEPTHFUNCNVPROC) (GLenum func); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOLORGENNVPROC) (GLenum color, GLenum genMode, GLenum colorFormat, const GLfloat *coeffs); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHTEXGENNVPROC) (GLenum texCoordSet, GLenum genMode, GLint components, const GLfloat *coeffs); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHFOGGENNVPROC) (GLenum genMode); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERFILLPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERSTROKEPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, GLint *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, GLfloat *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOMMANDSNVPROC) (GLuint path, GLubyte *commands); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOORDSNVPROC) (GLuint path, GLfloat *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHDASHARRAYNVPROC) (GLuint path, GLfloat *dashArray); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHMETRICSNVPROC) (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHMETRICRANGENVPROC) (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHSPACINGNVPROC) (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOLORGENIVNVPROC) (GLenum color, GLenum pname, GLint *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOLORGENFVNVPROC) (GLenum color, GLenum pname, GLfloat *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHTEXGENIVNVPROC) (GLenum texCoordSet, GLenum pname, GLint *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHTEXGENFVNVPROC) (GLenum texCoordSet, GLenum pname, GLfloat *value); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPOINTINFILLPATHNVPROC) (GLuint path, GLuint mask, GLfloat x, GLfloat y); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPOINTINSTROKEPATHNVPROC) (GLuint path, GLfloat x, GLfloat y); +typedef GLfloat (QOPENGLF_APIENTRYP PFNGLGETPATHLENGTHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLPOINTALONGPATHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOAD3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOAD3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULT3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULT3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHGLYPHINDEXRANGENVPROC) (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint baseAndCount[2]); +typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (QOPENGLF_APIENTRYP PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC) (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPROGRAMRESOURCEFVNVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLfloat *params); +#endif + +#ifndef GL_FLAT +#define GL_FLAT 0x1D00 +#endif + +#ifndef GL_INVERT +#define GL_INVERT 0x150A +#endif + +#ifndef GL_EXT_direct_state_access +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC) (GLenum mode); +#endif + +// When building on a system with GLES 2.0 or 3.0, we may still compile the NVPR +// code path even though it's never used. Keep it compiling by defining the +// necessary ES 3.1 separable program constants. +#ifndef GL_FRAGMENT_SHADER_BIT +#define GL_FRAGMENT_SHADER_BIT 0x00000002 +#endif +#ifndef GL_UNIFORM +#define GL_UNIFORM 0x92E1 +#endif + +class QQuickNvprFunctionsPrivate; + +class QQuickNvprFunctions +{ +public: + QQuickNvprFunctions(); + ~QQuickNvprFunctions(); + + static QSurfaceFormat format(); + static bool isSupported(); + + bool create(); + + bool createFragmentOnlyPipeline(const char *fragmentShaderSource, GLuint *pipeline, GLuint *program); + + PFNGLGENPATHSNVPROC genPaths = nullptr; + PFNGLDELETEPATHSNVPROC deletePaths = nullptr; + PFNGLISPATHNVPROC isPath = nullptr; + PFNGLPATHCOMMANDSNVPROC pathCommands = nullptr; + PFNGLPATHCOORDSNVPROC pathCoords = nullptr; + PFNGLPATHSUBCOMMANDSNVPROC pathSubCommands = nullptr; + PFNGLPATHSUBCOORDSNVPROC pathSubCoords = nullptr; + PFNGLPATHSTRINGNVPROC pathString = nullptr; + PFNGLPATHGLYPHSNVPROC pathGlyphs = nullptr; + PFNGLPATHGLYPHRANGENVPROC pathGlyphRange = nullptr; + PFNGLWEIGHTPATHSNVPROC weightPaths = nullptr; + PFNGLCOPYPATHNVPROC copyPath = nullptr; + PFNGLINTERPOLATEPATHSNVPROC interpolatePaths = nullptr; + PFNGLTRANSFORMPATHNVPROC transformPath = nullptr; + PFNGLPATHPARAMETERIVNVPROC pathParameteriv = nullptr; + PFNGLPATHPARAMETERINVPROC pathParameteri = nullptr; + PFNGLPATHPARAMETERFVNVPROC pathParameterfv = nullptr; + PFNGLPATHPARAMETERFNVPROC pathParameterf = nullptr; + PFNGLPATHDASHARRAYNVPROC pathDashArray = nullptr; + PFNGLPATHSTENCILFUNCNVPROC pathStencilFunc = nullptr; + PFNGLPATHSTENCILDEPTHOFFSETNVPROC pathStencilDepthOffset = nullptr; + PFNGLSTENCILFILLPATHNVPROC stencilFillPath = nullptr; + PFNGLSTENCILSTROKEPATHNVPROC stencilStrokePath = nullptr; + PFNGLSTENCILFILLPATHINSTANCEDNVPROC stencilFillPathInstanced = nullptr; + PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC stencilStrokePathInstanced = nullptr; + PFNGLPATHCOVERDEPTHFUNCNVPROC pathCoverDepthFunc = nullptr; + PFNGLPATHCOLORGENNVPROC pathColorGen = nullptr; + PFNGLPATHTEXGENNVPROC pathTexGen = nullptr; + PFNGLPATHFOGGENNVPROC pathFogGen = nullptr; + PFNGLCOVERFILLPATHNVPROC coverFillPath = nullptr; + PFNGLCOVERSTROKEPATHNVPROC coverStrokePath = nullptr; + PFNGLCOVERFILLPATHINSTANCEDNVPROC coverFillPathInstanced = nullptr; + PFNGLCOVERSTROKEPATHINSTANCEDNVPROC coverStrokePathInstanced = nullptr; + PFNGLGETPATHPARAMETERIVNVPROC getPathParameteriv = nullptr; + PFNGLGETPATHPARAMETERFVNVPROC getPathParameterfv = nullptr; + PFNGLGETPATHCOMMANDSNVPROC getPathCommands = nullptr; + PFNGLGETPATHCOORDSNVPROC getPathCoords = nullptr; + PFNGLGETPATHDASHARRAYNVPROC getPathDashArray = nullptr; + PFNGLGETPATHMETRICSNVPROC getPathMetrics = nullptr; + PFNGLGETPATHMETRICRANGENVPROC getPathMetricRange = nullptr; + PFNGLGETPATHSPACINGNVPROC getPathSpacing = nullptr; + PFNGLGETPATHCOLORGENIVNVPROC getPathColorgeniv = nullptr; + PFNGLGETPATHCOLORGENFVNVPROC getPathColorgenfv = nullptr; + PFNGLGETPATHTEXGENIVNVPROC getPathTexGeniv = nullptr; + PFNGLGETPATHTEXGENFVNVPROC getPathTexGenfv = nullptr; + PFNGLISPOINTINFILLPATHNVPROC isPointInFillPath = nullptr; + PFNGLISPOINTINSTROKEPATHNVPROC isPointInStrokePath = nullptr; + PFNGLGETPATHLENGTHNVPROC getPathLength = nullptr; + PFNGLPOINTALONGPATHNVPROC getPointAlongPath = nullptr; + PFNGLMATRIXLOAD3X2FNVPROC matrixLoad3x2f = nullptr; + PFNGLMATRIXLOAD3X3FNVPROC matrixLoad3x3f = nullptr; + PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC matrixLoadTranspose3x3f = nullptr; + PFNGLMATRIXMULT3X2FNVPROC matrixMult3x2f = nullptr; + PFNGLMATRIXMULT3X3FNVPROC matrixMult3x3f = nullptr; + PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC matrixMultTranspose3x3f = nullptr; + PFNGLSTENCILTHENCOVERFILLPATHNVPROC stencilThenCoverFillPath = nullptr; + PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC stencilThenCoverStrokePath = nullptr; + PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC stencilThenCoverFillPathInstanced = nullptr; + PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC stencilThenCoverStrokePathInstanced = nullptr; + PFNGLPATHGLYPHINDEXRANGENVPROC pathGlyphIndexRange = nullptr; + PFNGLPATHGLYPHINDEXARRAYNVPROC pathGlyphIndexArray = nullptr; + PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC pathMemoryGlyphIndexArray = nullptr; + PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC programPathFragmentInputGen = nullptr; + PFNGLGETPROGRAMRESOURCEFVNVPROC getProgramResourcefv = nullptr; + + PFNGLMATRIXLOADFEXTPROC matrixLoadf = nullptr; + PFNGLMATRIXLOADIDENTITYEXTPROC matrixLoadIdentity = nullptr; + +private: + QQuickNvprFunctionsPrivate *d; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_OPENGL + +#endif // QQUICKNVPRFUNCTIONS_P_H diff --git a/src/imports/pathitem/qquicknvprfunctions_p_p.h b/src/imports/pathitem/qquicknvprfunctions_p_p.h new file mode 100644 index 0000000000..6df20566af --- /dev/null +++ b/src/imports/pathitem/qquicknvprfunctions_p_p.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKNVPRFUNCTIONS_P_P_H +#define QQUICKNVPRFUNCTIONS_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qquicknvprfunctions_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickNvprFunctionsPrivate +{ +public: + QQuickNvprFunctionsPrivate(QQuickNvprFunctions *q_ptr) : q(q_ptr) { } + + bool resolve(); + + QQuickNvprFunctions *q; +}; + +QT_END_NAMESPACE + +#endif // QQUICKNVPRFUNCTIONS_P_P_H diff --git a/src/imports/pathitem/qquickpathitem.cpp b/src/imports/pathitem/qquickpathitem.cpp new file mode 100644 index 0000000000..5255a55798 --- /dev/null +++ b/src/imports/pathitem/qquickpathitem.cpp @@ -0,0 +1,2258 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickpathitem_p.h" +#include "qquickpathitem_p_p.h" +#include "qquickpathitemgenericrenderer_p.h" +#include "qquickpathitemnvprrenderer_p.h" +#include "qquickpathitemsoftwarerenderer_p.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QQuickPathItemStrokeFillParams::QQuickPathItemStrokeFillParams() + : strokeColor(Qt::white), + strokeWidth(1), + fillColor(Qt::white), + fillRule(QQuickVisualPath::OddEvenFill), + joinStyle(QQuickVisualPath::BevelJoin), + miterLimit(2), + capStyle(QQuickVisualPath::SquareCap), + strokeStyle(QQuickVisualPath::SolidLine), + dashOffset(0), + fillGradient(nullptr) +{ + dashPattern << 4 << 2; // 4 * strokeWidth dash followed by 2 * strokeWidth space +} + +QPainterPath QQuickPathItemPath::toPainterPath() const +{ + QPainterPath p; + int coordIdx = 0; + for (int i = 0; i < cmd.count(); ++i) { + switch (cmd[i]) { + case QQuickPathItemPath::MoveTo: + p.moveTo(coords[coordIdx], coords[coordIdx + 1]); + coordIdx += 2; + break; + case QQuickPathItemPath::LineTo: + p.lineTo(coords[coordIdx], coords[coordIdx + 1]); + coordIdx += 2; + break; + case QQuickPathItemPath::QuadTo: + p.quadTo(coords[coordIdx], coords[coordIdx + 1], + coords[coordIdx + 2], coords[coordIdx + 3]); + coordIdx += 4; + break; + case QQuickPathItemPath::CubicTo: + p.cubicTo(coords[coordIdx], coords[coordIdx + 1], + coords[coordIdx + 2], coords[coordIdx + 3], + coords[coordIdx + 4], coords[coordIdx + 5]); + coordIdx += 6; + break; + case QQuickPathItemPath::ArcTo: + // does not map to the QPainterPath API; reuse the helper code from QQuickSvgParser + QQuickSvgParser::pathArc(p, + coords[coordIdx], coords[coordIdx + 1], // radius + coords[coordIdx + 2], // xAxisRotation + !qFuzzyIsNull(coords[coordIdx + 6]), // useLargeArc + !qFuzzyIsNull(coords[coordIdx + 5]), // sweep flag + coords[coordIdx + 3], coords[coordIdx + 4], // end + p.currentPosition().x(), p.currentPosition().y()); + coordIdx += 7; + break; + default: + qWarning("Unknown JS path command: %d", cmd[i]); + break; + } + } + return p; +} + +/*! + \qmltype VisualPath + \instantiates QQuickVisualPath + \inqmlmodule QtQuick + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Object + \brief Describes a Path and associated properties for stroking and filling + \since 5.10 + + A PathItem contains one or more VisualPath elements. At least one + VisualPath is necessary in order to have a PathItem output anything + visible. A VisualPath in turn contains a Path and properties describing the + stroking and filling parameters, such as the stroke width and color, the + fill color or gradient, join and cap styles, and so on. Finally, the Path + object contains a list of path elements like PathMove, PathLine, PathCubic, + PathQuad, PathArc. + + Any property changes in these data sets will be bubble up and change the + output of the PathItem. This means that it is simple and easy to change, or + even animate, the starting and ending position, control points, or any + stroke or fill parameters using the usual QML bindings and animation types + like NumberAnimation. + + In the following example the line join style changes automatically based on + the value of joinStyleIndex: + + \code + VisualPath { + strokeColor: "black" + strokeWidth: 16 + fillColor: "transparent" + capStyle: VisualPath.RoundCap + + property int joinStyleIndex: 0 + property variant styles: [ VisualPath.BevelJoin, VisualPath.MiterJoin, VisualPath.RoundJoin ] + + joinStyle: styles[joinStyleIndex] + + Path { + startX: 30 + startY: 30 + PathLine { x: 100; y: 100 } + PathLine { x: 30; y: 100 } + } + } + \endcode + + Once associated with a PathItem, here is the output with a joinStyleIndex + of 2 (VisualPath.RoundJoin): + + \image visualpath-code-example.png + */ + +QQuickVisualPathPrivate::QQuickVisualPathPrivate() + : path(nullptr), + dirty(DirtyAll) +{ +} + +QQuickVisualPath::QQuickVisualPath(QObject *parent) + : QObject(*(new QQuickVisualPathPrivate), parent) +{ +} + +QQuickVisualPath::~QQuickVisualPath() +{ +} + +/*! + \qmlproperty Path QtQuick::VisualPath::path + + This property holds the Path object. + + \default + */ + +QQuickPath *QQuickVisualPath::path() const +{ + Q_D(const QQuickVisualPath); + return d->path; +} + +void QQuickVisualPath::setPath(QQuickPath *path) +{ + Q_D(QQuickVisualPath); + if (d->path == path) + return; + + if (d->path) + qmlobject_disconnect(d->path, QQuickPath, SIGNAL(changed()), + this, QQuickVisualPath, SLOT(_q_pathChanged())); + d->path = path; + qmlobject_connect(d->path, QQuickPath, SIGNAL(changed()), + this, QQuickVisualPath, SLOT(_q_pathChanged())); + + d->dirty |= QQuickVisualPathPrivate::DirtyPath; + emit pathChanged(); + emit changed(); +} + +void QQuickVisualPathPrivate::_q_pathChanged() +{ + Q_Q(QQuickVisualPath); + dirty |= DirtyPath; + emit q->changed(); +} + +/*! + \qmlproperty color QtQuick::VisualPath::strokeColor + + This property holds the stroking color. + + When set to \c transparent, no stroking occurs. + + The default value is \c white. + */ + +QColor QQuickVisualPath::strokeColor() const +{ + Q_D(const QQuickVisualPath); + return d->sfp.strokeColor; +} + +void QQuickVisualPath::setStrokeColor(const QColor &color) +{ + Q_D(QQuickVisualPath); + if (d->sfp.strokeColor != color) { + d->sfp.strokeColor = color; + d->dirty |= QQuickVisualPathPrivate::DirtyStrokeColor; + emit strokeColorChanged(); + emit changed(); + } +} + +/*! + \qmlproperty color QtQuick::VisualPath::strokeWidth + + This property holds the stroke width. + + When set to a negative value, no stroking occurs. + + The default value is 1. + */ + +qreal QQuickVisualPath::strokeWidth() const +{ + Q_D(const QQuickVisualPath); + return d->sfp.strokeWidth; +} + +void QQuickVisualPath::setStrokeWidth(qreal w) +{ + Q_D(QQuickVisualPath); + if (d->sfp.strokeWidth != w) { + d->sfp.strokeWidth = w; + d->dirty |= QQuickVisualPathPrivate::DirtyStrokeWidth; + emit strokeWidthChanged(); + emit changed(); + } +} + +/*! + \qmlproperty color QtQuick::VisualPath::fillColor + + This property holds the fill color. + + When set to \c transparent, no filling occurs. + + The default value is \c white. + */ + +QColor QQuickVisualPath::fillColor() const +{ + Q_D(const QQuickVisualPath); + return d->sfp.fillColor; +} + +void QQuickVisualPath::setFillColor(const QColor &color) +{ + Q_D(QQuickVisualPath); + if (d->sfp.fillColor != color) { + d->sfp.fillColor = color; + d->dirty |= QQuickVisualPathPrivate::DirtyFillColor; + emit fillColorChanged(); + emit changed(); + } +} + +/*! + \qmlproperty enumeration QtQuick::VisualPath::fillRule + + This property holds the fill rule. The default value is + VisualPath.OddEvenFill. For an example on fill rules, see + QPainterPath::setFillRule(). + + \list + \li VisualPath.OddEvenFill + \li VisualPath.WindingFill + \endlist + */ + +QQuickVisualPath::FillRule QQuickVisualPath::fillRule() const +{ + Q_D(const QQuickVisualPath); + return d->sfp.fillRule; +} + +void QQuickVisualPath::setFillRule(FillRule fillRule) +{ + Q_D(QQuickVisualPath); + if (d->sfp.fillRule != fillRule) { + d->sfp.fillRule = fillRule; + d->dirty |= QQuickVisualPathPrivate::DirtyFillRule; + emit fillRuleChanged(); + emit changed(); + } +} + +/*! + \qmlproperty enumeration QtQuick::VisualPath::joinStyle + + This property defines how joins between two connected lines are drawn. The + default value is VisualPath.BevelJoin. + + \list + \li VisualPath.MiterJoin - The outer edges of the lines are extended to meet at an angle, and this area is filled. + \li VisualPath.BevelJoin - The triangular notch between the two lines is filled. + \li VisualPath.RoundJoin - A circular arc between the two lines is filled. + \endlist + */ + +QQuickVisualPath::JoinStyle QQuickVisualPath::joinStyle() const +{ + Q_D(const QQuickVisualPath); + return d->sfp.joinStyle; +} + +void QQuickVisualPath::setJoinStyle(JoinStyle style) +{ + Q_D(QQuickVisualPath); + if (d->sfp.joinStyle != style) { + d->sfp.joinStyle = style; + d->dirty |= QQuickVisualPathPrivate::DirtyStyle; + emit joinStyleChanged(); + emit changed(); + } +} + +/*! + \qmlproperty int QtQuick::VisualPath::miterLimit + + When VisualPath.joinStyle is set to VisualPath.MiterJoin, this property + specifies how far the miter join can extend from the join point. + + The default value is 2. + */ + +int QQuickVisualPath::miterLimit() const +{ + Q_D(const QQuickVisualPath); + return d->sfp.miterLimit; +} + +void QQuickVisualPath::setMiterLimit(int limit) +{ + Q_D(QQuickVisualPath); + if (d->sfp.miterLimit != limit) { + d->sfp.miterLimit = limit; + d->dirty |= QQuickVisualPathPrivate::DirtyStyle; + emit miterLimitChanged(); + emit changed(); + } +} + +/*! + \qmlproperty enumeration QtQuick::VisualPath::capStyle + + This property defines how the end points of lines are drawn. The + default value is VisualPath.SquareCap. + + \list + \li VisualPath.FlatCap - A square line end that does not cover the end point of the line. + \li VisualPath.SquareCap - A square line end that covers the end point and extends beyond it by half the line width. + \li VisualPath.RoundCap - A rounded line end. + \endlist + */ + +QQuickVisualPath::CapStyle QQuickVisualPath::capStyle() const +{ + Q_D(const QQuickVisualPath); + return d->sfp.capStyle; +} + +void QQuickVisualPath::setCapStyle(CapStyle style) +{ + Q_D(QQuickVisualPath); + if (d->sfp.capStyle != style) { + d->sfp.capStyle = style; + d->dirty |= QQuickVisualPathPrivate::DirtyStyle; + emit capStyleChanged(); + emit changed(); + } +} + +/*! + \qmlproperty enumeration QtQuick::VisualPath::strokeStyle + + This property defines the style of stroking. The default value is + VisualPath.SolidLine. + + \list + \li VisualPath.SolidLine - A plain line. + \li VisualPath.DashLine - Dashes separated by a few pixels. + \endlist + */ + +QQuickVisualPath::StrokeStyle QQuickVisualPath::strokeStyle() const +{ + Q_D(const QQuickVisualPath); + return d->sfp.strokeStyle; +} + +void QQuickVisualPath::setStrokeStyle(StrokeStyle style) +{ + Q_D(QQuickVisualPath); + if (d->sfp.strokeStyle != style) { + d->sfp.strokeStyle = style; + d->dirty |= QQuickVisualPathPrivate::DirtyDash; + emit strokeStyleChanged(); + emit changed(); + } +} + +/*! + \qmlproperty real QtQuick::VisualPath::dashOffset + + This property defines the starting point on the dash pattern, measured in + units used to specify the dash pattern. + + The default value is 0. + + \sa QPen::setDashOffset() + */ + +qreal QQuickVisualPath::dashOffset() const +{ + Q_D(const QQuickVisualPath); + return d->sfp.dashOffset; +} + +void QQuickVisualPath::setDashOffset(qreal offset) +{ + Q_D(QQuickVisualPath); + if (d->sfp.dashOffset != offset) { + d->sfp.dashOffset = offset; + d->dirty |= QQuickVisualPathPrivate::DirtyDash; + emit dashOffsetChanged(); + emit changed(); + } +} + +/*! + \qmlproperty list QtQuick::VisualPath::dashPattern + + This property defines the dash pattern when VisualPath.strokeStyle is set + to VisualPath.DashLine. The pattern must be specified as an even number of + positive entries where the entries 1, 3, 5... are the dashes and 2, 4, 6... + are the spaces. The pattern is specified in units of the pen's width. + + The default value is (4, 2), meaning a dash of 4 * VisualPath.strokeWidth + pixels followed by a space of 2 * VisualPath.strokeWidth pixels. + + \sa QPen::setDashPattern() + */ + +QVector QQuickVisualPath::dashPattern() const +{ + Q_D(const QQuickVisualPath); + return d->sfp.dashPattern; +} + +void QQuickVisualPath::setDashPattern(const QVector &array) +{ + Q_D(QQuickVisualPath); + if (d->sfp.dashPattern != array) { + d->sfp.dashPattern = array; + d->dirty |= QQuickVisualPathPrivate::DirtyDash; + emit dashPatternChanged(); + emit changed(); + } +} + +/*! + \qmlproperty PathGradient QtQuick::VisualPath::fillGradient + + This property defines the fill gradient. By default no gradient is enabled + and the value is \c null. In this case the fill uses a solid color based on + the value of VisuaLPath.fillColor. + + When set, VisualPath.fillColor is ignored and filling is done using one of + the PathGradient subtypes. + */ + +QQuickPathGradient *QQuickVisualPath::fillGradient() const +{ + Q_D(const QQuickVisualPath); + return d->sfp.fillGradient; +} + +void QQuickVisualPath::setFillGradient(QQuickPathGradient *gradient) +{ + Q_D(QQuickVisualPath); + if (d->sfp.fillGradient != gradient) { + if (d->sfp.fillGradient) + qmlobject_disconnect(d->sfp.fillGradient, QQuickPathGradient, SIGNAL(updated()), + this, QQuickVisualPath, SLOT(_q_fillGradientChanged())); + d->sfp.fillGradient = gradient; + if (d->sfp.fillGradient) + qmlobject_connect(d->sfp.fillGradient, QQuickPathGradient, SIGNAL(updated()), + this, QQuickVisualPath, SLOT(_q_fillGradientChanged())); + d->dirty |= QQuickVisualPathPrivate::DirtyFillGradient; + emit changed(); + } +} + +void QQuickVisualPathPrivate::_q_fillGradientChanged() +{ + Q_Q(QQuickVisualPath); + dirty |= DirtyFillGradient; + emit q->changed(); +} + +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 a GPU vendor 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 + PathItem is suitable for creating shapes spreading over larger areas of the + screen, avoiding the performance penalty for texture uploads or framebuffer + blits. In addition, the declarative API allows manipulating, binding to, + and even animating the path element properties like starting and ending + position, the control points, etc. + + 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, PathSvg. + + See \l Path for a detailed overview of the supported path elements. + + \code + PathItem { + width: 200 + height: 150 + anchors.centerIn: parent + VisualPath { + strokeWidth: 4 + strokeColor: "red" + fillGradient: PathLinearGradient { + x1: 20; y1: 20 + x2: 180; y2: 130 + PathGradientStop { position: 0; color: "blue" } + PathGradientStop { position: 0.2; color: "green" } + PathGradientStop { position: 0.4; color: "red" } + PathGradientStop { position: 0.6; color: "yellow" } + PathGradientStop { position: 1; color: "cyan" } + } + strokeStyle: VisualPath.DashLine + dashPattern: [ 1, 4 ] + Path { + startX: 20; startY: 20 + PathLine { x: 180; y: 130 } + PathLine { x: 20; y: 130 } + PathLine { x: 20; y: 20 } + } + } + } + \endcode + + \image pathitem-code-example.png + + \note 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. + + The following list summarizes the available PathItem rendering approaches: + + \list + + \li When running with the default, OpenGL backend of Qt Quick, both the + generic, triangulation-based and the NVIDIA-specific + \c{GL_NV_path_rendering} methods are available. The choice is made at + runtime, depending on the graphics driver's capabilities. When this is not + desired, applications can force using the generic method by setting the + PathItem.enableVendorExtensions property to \c false. + + \li The \c software backend is fully supported. The path is rendered via + QPainter::strokePath() and QPainter::fillPath() in this case. + + \li The Direct 3D 12 backend is not currently supported. + + \li The OpenVG backend is not currently supported. + + \endlist + + \sa Path, PathMove, PathLine, PathQuad, PathCubic, PathArc, PathSvg +*/ + +QQuickPathItemPrivate::QQuickPathItemPrivate() + : componentComplete(true), + vpChanged(false), + rendererType(QQuickPathItem::UnknownRenderer), + async(false), + status(QQuickPathItem::Null), + renderer(nullptr), + enableVendorExts(true) +{ +} + +QQuickPathItemPrivate::~QQuickPathItemPrivate() +{ + delete renderer; +} + +void QQuickPathItemPrivate::_q_visualPathChanged() +{ + Q_Q(QQuickPathItem); + vpChanged = true; + q->polish(); +} + +void QQuickPathItemPrivate::setStatus(QQuickPathItem::Status newStatus) +{ + Q_Q(QQuickPathItem); + if (status != newStatus) { + status = newStatus; + emit q->statusChanged(); + } +} + +QQuickPathItem::QQuickPathItem(QQuickItem *parent) + : QQuickItem(*(new QQuickPathItemPrivate), parent) +{ + setFlag(ItemHasContents); +} + +QQuickPathItem::~QQuickPathItem() +{ +} + +/*! + \qmlproperty enumeration QtQuick::PathItem::rendererType + + This property determines which path rendering backend is active. + + \list + + \li PathItem.UnknownRenderer - The renderer is unknown. + + \li PathItem.GeometryRenderer - The generic, driver independent solution + for OpenGL. Uses the same CPU-based triangulation approach as QPainter's + OpenGL 2 paint engine. This is the default on non-NVIDIA hardware when the + default, OpenGL Qt Quick scenegraph backend is in use. + + \li PathItem.NvprRenderer - Path items are rendered by performing OpenGL + calls using the \c{GL_NV_path_rendering} extension. This is the default on + NVIDIA hardware when the default, OpenGL Qt Quick scenegraph backend is in + use. + + \li PathItem.SoftwareRenderer - Pure QPainter drawing using the raster + paint engine. This is the default, and only, option when the Qt Quick + scenegraph is running with the \c software backend. + + \endlist +*/ + +QQuickPathItem::RendererType QQuickPathItem::rendererType() const +{ + Q_D(const QQuickPathItem); + return d->rendererType; +} + +/*! + \qmlproperty bool QtQuick::PathItem::asynchronous + + When PathItem.rendererType is PathItem.GeometryRenderer, the input path is + triangulated on the CPU during the polishing phase of the PathItem. This is + potentially expensive. To offload this work to separate worker threads, set + this property to \c true. + + When enabled, making a PathItem visible will not wait for the content to + become available. Instead, the gui/main thread is not blocked and the + results of the path rendering are shown only when all the asynchronous work + has been finished. + + The default value is \c false. + */ + +bool QQuickPathItem::asynchronous() const +{ + Q_D(const QQuickPathItem); + return d->async; +} + +void QQuickPathItem::setAsynchronous(bool async) +{ + Q_D(QQuickPathItem); + if (d->async != async) { + d->async = async; + emit asynchronousChanged(); + if (d->componentComplete) + d->_q_visualPathChanged(); + } +} + +/*! + \qmlproperty bool QtQuick::PathItem::enableVendorExtensions + + This property controls the usage of non-standard OpenGL extensions like + GL_NV_path_rendering. To disable PathItem.NvprRenderer and force a uniform + behavior regardless of the graphics card and drivers, set this property to + \c false. + + The default value is \c true. + */ + +bool QQuickPathItem::enableVendorExtensions() const +{ + Q_D(const QQuickPathItem); + return d->enableVendorExts; +} + +void QQuickPathItem::setEnableVendorExtensions(bool enable) +{ + Q_D(QQuickPathItem); + if (d->enableVendorExts != enable) { + d->enableVendorExts = enable; + emit enableVendorExtensionsChanged(); + } +} + +/*! + \qmlproperty enumeration QtQuick::PathItem::status + + This property determines the status of the PathItem and is relevant when + PathItem.asynchronous is set to \c true. + + \list + + \li PathItem.Null - Not yet initialized. + + \li PathItem.Ready - The PathItem has finished processing. + + \li PathItem.Processing - The path is being processed. + + \endlist + */ + +QQuickPathItem::Status QQuickPathItem::status() const +{ + Q_D(const QQuickPathItem); + return d->status; +} + +static QQuickVisualPath *vpe_at(QQmlListProperty *property, int index) +{ + QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast(property->object)); + return d->qmlData.vp.at(index); +} + +static void vpe_append(QQmlListProperty *property, QQuickVisualPath *obj) +{ + QQuickPathItem *item = static_cast(property->object); + QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(item); + d->qmlData.vp.append(obj); + + if (d->componentComplete) { + QObject::connect(obj, SIGNAL(changed()), item, SLOT(_q_visualPathChanged())); + d->_q_visualPathChanged(); + } +} + +static int vpe_count(QQmlListProperty *property) +{ + QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast(property->object)); + return d->qmlData.vp.count(); +} + +static void vpe_clear(QQmlListProperty *property) +{ + QQuickPathItem *item = static_cast(property->object); + QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(item); + + for (QQuickVisualPath *p : d->qmlData.vp) + QObject::disconnect(p, SIGNAL(changed()), item, SLOT(_q_visualPathChanged())); + + d->qmlData.vp.clear(); + + if (d->componentComplete) + d->_q_visualPathChanged(); +} + +/*! + \qmlproperty list QtQuick::PathItem::elements + + This property holds the VisualPath objects that define the contents of the + PathItem. + + \default + */ + +QQmlListProperty QQuickPathItem::elements() +{ + return QQmlListProperty(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->qmlData.vp) + connect(p, SIGNAL(changed()), this, SLOT(_q_visualPathChanged())); + + d->_q_visualPathChanged(); +} + +void QQuickPathItem::updatePolish() +{ + Q_D(QQuickPathItem); + + if (!d->vpChanged) + return; + + d->vpChanged = false; + + if (!d->renderer) { + d->createRenderer(); + if (!d->renderer) + return; + emit rendererChanged(); + } + + // endSync() is where expensive calculations may happen (or get kicked off + // on worker threads), depending on the backend. Therefore do this only + // when the item is visible. + if (isVisible()) + d->sync(); + + update(); +} + +void QQuickPathItem::itemChange(ItemChange change, const ItemChangeData &data) +{ + Q_D(QQuickPathItem); + + // sync may have been deferred; do it now if the item became visible + if (change == ItemVisibleHasChanged && data.boolValue) + d->_q_visualPathChanged(); + + QQuickItem::itemChange(change, data); +} + +QSGNode *QQuickPathItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *) +{ + // Called on the render thread, with the gui thread blocked. We can now + // safely access gui thread data. + + Q_D(QQuickPathItem); + if (d->renderer) { + if (!node) + node = d->createNode(); + d->renderer->updateNode(); + } + return node; +} + +// the renderer object lives on the gui thread +void QQuickPathItemPrivate::createRenderer() +{ + Q_Q(QQuickPathItem); + QSGRendererInterface *ri = q->window()->rendererInterface(); + if (!ri) + return; + + switch (ri->graphicsApi()) { +#ifndef QT_NO_OPENGL + case QSGRendererInterface::OpenGL: + if (enableVendorExts && QQuickPathItemNvprRenderNode::isSupported()) { + rendererType = QQuickPathItem::NvprRenderer; + renderer = new QQuickPathItemNvprRenderer; + } else { + rendererType = QQuickPathItem::GeometryRenderer; + renderer = new QQuickPathItemGenericRenderer(q); + } + break; +#endif + case QSGRendererInterface::Software: + rendererType = QQuickPathItem::SoftwareRenderer; + renderer = new QQuickPathItemSoftwareRenderer; + break; + default: + qWarning("No path backend for this graphics API yet"); + break; + } +} + +// the node lives on the render thread +QSGNode *QQuickPathItemPrivate::createNode() +{ + Q_Q(QQuickPathItem); + QSGNode *node = nullptr; + if (!q->window()) + return node; + QSGRendererInterface *ri = q->window()->rendererInterface(); + if (!ri) + return node; + + switch (ri->graphicsApi()) { +#ifndef QT_NO_OPENGL + case QSGRendererInterface::OpenGL: + if (enableVendorExts && QQuickPathItemNvprRenderNode::isSupported()) { + node = new QQuickPathItemNvprRenderNode; + static_cast(renderer)->setNode( + static_cast(node)); + } else { + node = new QQuickPathItemGenericNode; + static_cast(renderer)->setRootNode( + static_cast(node)); + } + break; +#endif + case QSGRendererInterface::Software: + node = new QQuickPathItemSoftwareRenderNode(q); + static_cast(renderer)->setNode( + static_cast(node)); + break; + default: + qWarning("No path backend for this graphics API yet"); + break; + } + + return node; +} + +static void q_asyncPathItemReady(void *data) +{ + QQuickPathItemPrivate *self = static_cast(data); + self->setStatus(QQuickPathItem::Ready); +} + +void QQuickPathItemPrivate::sync() +{ + const bool useAsync = async && renderer->flags().testFlag(QQuickAbstractPathRenderer::SupportsAsync); + if (useAsync) { + setStatus(QQuickPathItem::Processing); + renderer->setAsyncCallback(q_asyncPathItemReady, this); + } + + if (!jsData.isValid()) { + // Standard route: The path and stroke/fill parameters are provided via + // VisualPath and Path. + const int count = qmlData.vp.count(); + renderer->beginSync(count); + + for (int i = 0; i < count; ++i) { + QQuickVisualPath *p = qmlData.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; + } + + renderer->endSync(useAsync); + } else { + // Path and stroke/fill params provided from JavaScript. This avoids + // QObjects at the expense of not supporting changes afterwards. + const int count = jsData.paths.count(); + renderer->beginSync(count); + + for (int i = 0; i < count; ++i) { + renderer->setJSPath(i, jsData.paths[i]); + const QQuickPathItemStrokeFillParams sfp(jsData.sfp[i]); + renderer->setStrokeColor(i, sfp.strokeColor); + renderer->setStrokeWidth(i, sfp.strokeWidth); + renderer->setFillColor(i, sfp.fillColor); + renderer->setFillRule(i, sfp.fillRule); + renderer->setJoinStyle(i, sfp.joinStyle, sfp.miterLimit); + renderer->setCapStyle(i, sfp.capStyle); + renderer->setStrokeStyle(i, sfp.strokeStyle, sfp.dashOffset, sfp.dashPattern); + renderer->setFillGradient(i, sfp.fillGradient); + } + + renderer->endSync(useAsync); + } + + if (!useAsync) + setStatus(QQuickPathItem::Ready); +} + +// ***** gradient support ***** + +/*! + \qmltype PathGradientStop + \instantiates QQuickPathGradientStop + \inqmlmodule QtQuick + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Object + \brief Defines a color at a position in a gradient + \since 5.10 + */ + +QQuickPathGradientStop::QQuickPathGradientStop(QObject *parent) + : QObject(parent), + m_position(0), + m_color(Qt::black) +{ +} + +/*! + \qmlproperty real QtQuick::PathGradientStop::position + + The position and color properties describe the color used at a given + position in a gradient, as represented by a gradient stop. + + The default value is 0. + */ + +qreal QQuickPathGradientStop::position() const +{ + return m_position; +} + +void QQuickPathGradientStop::setPosition(qreal position) +{ + if (m_position != position) { + m_position = position; + if (QQuickPathGradient *grad = qobject_cast(parent())) + emit grad->updated(); + } +} + +/*! + \qmlproperty real QtQuick::PathGradientStop::color + + The position and color properties describe the color used at a given + position in a gradient, as represented by a gradient stop. + + The default value is \c black. + */ + +QColor QQuickPathGradientStop::color() const +{ + return m_color; +} + +void QQuickPathGradientStop::setColor(const QColor &color) +{ + if (m_color != color) { + m_color = color; + if (QQuickPathGradient *grad = qobject_cast(parent())) + emit grad->updated(); + } +} + +/*! + \qmltype PathGradient + \instantiates QQuickPathGradient + \inqmlmodule QtQuick + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Object + \brief Base type of PathItem fill gradients + \since 5.10 + + This is an abstract base class for gradients like PathLinearGradient and + cannot be created directly. + */ + +QQuickPathGradient::QQuickPathGradient(QObject *parent) + : QObject(parent), + m_spread(PadSpread) +{ +} + +int QQuickPathGradient::countStops(QQmlListProperty *list) +{ + QQuickPathGradient *grad = qobject_cast(list->object); + Q_ASSERT(grad); + return grad->m_stops.count(); +} + +QObject *QQuickPathGradient::atStop(QQmlListProperty *list, int index) +{ + QQuickPathGradient *grad = qobject_cast(list->object); + Q_ASSERT(grad); + return grad->m_stops.at(index); +} + +void QQuickPathGradient::appendStop(QQmlListProperty *list, QObject *stop) +{ + QQuickPathGradientStop *sstop = qobject_cast(stop); + if (!sstop) { + qWarning("Gradient stop list only supports QQuickPathGradientStop elements"); + return; + } + QQuickPathGradient *grad = qobject_cast(list->object); + Q_ASSERT(grad); + sstop->setParent(grad); + grad->m_stops.append(sstop); +} + +/*! + \qmlproperty list QtQuick::PathGradient::stops + \default + + The list of PathGradientStop objects defining the colors at given positions + in the gradient. + */ + +QQmlListProperty QQuickPathGradient::stops() +{ + return QQmlListProperty(this, nullptr, + &QQuickPathGradient::appendStop, + &QQuickPathGradient::countStops, + &QQuickPathGradient::atStop, + nullptr); +} + +QGradientStops QQuickPathGradient::sortedGradientStops() const +{ + QGradientStops result; + for (int i = 0; i < m_stops.count(); ++i) { + QQuickPathGradientStop *s = static_cast(m_stops[i]); + int j = 0; + while (j < result.count() && result[j].first < s->position()) + ++j; + result.insert(j, QGradientStop(s->position(), s->color())); + } + return result; +} + +/*! + \qmlproperty enumeration QtQuick::PathGradient::spred + + Specifies how the area outside the gradient area should be filled. The + default value is PathGradient.PadSpread. + + \list + \li PathGradient.PadSpread - The area is filled with the closest stop color. + \li PathGradient.RepeatSpread - The gradient is repeated outside the gradient area. + \li PathGradient.ReflectSpread - The gradient is reflected outside the gradient area. + \endlist + */ + +QQuickPathGradient::SpreadMode QQuickPathGradient::spread() const +{ + return m_spread; +} + +void QQuickPathGradient::setSpread(SpreadMode mode) +{ + if (m_spread != mode) { + m_spread = mode; + emit spreadChanged(); + emit updated(); + } +} + +/*! + \qmltype PathLinearGradient + \instantiates QQuickPathLinearGradient + \inqmlmodule QtQuick + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits PathGradient + \brief Linear gradient + \since 5.10 + + Linear gradients interpolate colors between start and end points. Outside + these points the gradient is either padded, reflected or repeated depending + on the spread type. + + \sa QLinearGradient + */ + +QQuickPathLinearGradient::QQuickPathLinearGradient(QObject *parent) + : QQuickPathGradient(parent) +{ +} + +/*! + \qmlproperty real QtQuick::PathLinearGradient::x1 + \qmlproperty real QtQuick::PathLinearGradient::y1 + \qmlproperty real QtQuick::PathLinearGradient::x2 + \qmlproperty real QtQuick::PathLinearGradient::y2 + + These properties define the start and end points between which color + interpolation occurs. By default both the stard and end points are set to + (0, 0). + */ + +qreal QQuickPathLinearGradient::x1() const +{ + return m_start.x(); +} + +void QQuickPathLinearGradient::setX1(qreal v) +{ + if (m_start.x() != v) { + m_start.setX(v); + emit x1Changed(); + emit updated(); + } +} + +qreal QQuickPathLinearGradient::y1() const +{ + return m_start.y(); +} + +void QQuickPathLinearGradient::setY1(qreal v) +{ + if (m_start.y() != v) { + m_start.setY(v); + emit y1Changed(); + emit updated(); + } +} + +qreal QQuickPathLinearGradient::x2() const +{ + return m_end.x(); +} + +void QQuickPathLinearGradient::setX2(qreal v) +{ + if (m_end.x() != v) { + m_end.setX(v); + emit x2Changed(); + emit updated(); + } +} + +qreal QQuickPathLinearGradient::y2() const +{ + return m_end.y(); +} + +void QQuickPathLinearGradient::setY2(qreal v) +{ + if (m_end.y() != v) { + m_end.setY(v); + emit y2Changed(); + emit updated(); + } +} + +#ifndef QT_NO_OPENGL + +// contexts sharing with each other get the same cache instance +class QQuickPathItemGradientCacheWrapper +{ +public: + QQuickPathItemGradientCache *get(QOpenGLContext *context) + { + return m_resource.value(context); + } + +private: + QOpenGLMultiGroupSharedResource m_resource; +}; + +QQuickPathItemGradientCache *QQuickPathItemGradientCache::currentCache() +{ + static QQuickPathItemGradientCacheWrapper qt_path_gradient_caches; + return qt_path_gradient_caches.get(QOpenGLContext::currentContext()); +} + +// let QOpenGLContext manage the lifetime of the cached textures +QQuickPathItemGradientCache::~QQuickPathItemGradientCache() +{ + m_cache.clear(); +} + +void QQuickPathItemGradientCache::invalidateResource() +{ + m_cache.clear(); +} + +void QQuickPathItemGradientCache::freeResource(QOpenGLContext *) +{ + qDeleteAll(m_cache); + m_cache.clear(); +} + +static void generateGradientColorTable(const QQuickPathItemGradientCache::GradientDesc &gradient, + uint *colorTable, int size, float opacity) +{ + int pos = 0; + const QGradientStops &s = gradient.stops; + const bool colorInterpolation = true; + + uint alpha = qRound(opacity * 256); + uint current_color = ARGB_COMBINE_ALPHA(s[0].second.rgba(), alpha); + qreal incr = 1.0 / qreal(size); + qreal fpos = 1.5 * incr; + colorTable[pos++] = ARGB2RGBA(qPremultiply(current_color)); + + while (fpos <= s.first().first) { + colorTable[pos] = colorTable[pos - 1]; + pos++; + fpos += incr; + } + + if (colorInterpolation) + current_color = qPremultiply(current_color); + + const int sLast = s.size() - 1; + for (int i = 0; i < sLast; ++i) { + qreal delta = 1/(s[i+1].first - s[i].first); + uint next_color = ARGB_COMBINE_ALPHA(s[i + 1].second.rgba(), alpha); + if (colorInterpolation) + next_color = qPremultiply(next_color); + + while (fpos < s[i+1].first && pos < size) { + int dist = int(256 * ((fpos - s[i].first) * delta)); + int idist = 256 - dist; + if (colorInterpolation) + colorTable[pos] = ARGB2RGBA(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist)); + else + colorTable[pos] = ARGB2RGBA(qPremultiply(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist))); + ++pos; + fpos += incr; + } + current_color = next_color; + } + + Q_ASSERT(s.size() > 0); + + uint last_color = ARGB2RGBA(qPremultiply(ARGB_COMBINE_ALPHA(s[sLast].second.rgba(), alpha))); + for ( ; pos < size; ++pos) + colorTable[pos] = last_color; + + colorTable[size-1] = last_color; +} + +QSGTexture *QQuickPathItemGradientCache::get(const GradientDesc &grad) +{ + QSGPlainTexture *tx = m_cache[grad]; + if (!tx) { + QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); + GLuint id; + f->glGenTextures(1, &id); + f->glBindTexture(GL_TEXTURE_2D, id); + static const uint W = 1024; // texture size is 1024x1 + uint buf[W]; + generateGradientColorTable(grad, buf, W, 1.0f); + f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, W, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf); + tx = new QSGPlainTexture; + tx->setTextureId(id); + switch (grad.spread) { + case QQuickPathGradient::PadSpread: + tx->setHorizontalWrapMode(QSGTexture::ClampToEdge); + tx->setVerticalWrapMode(QSGTexture::ClampToEdge); + break; + case QQuickPathGradient::RepeatSpread: + tx->setHorizontalWrapMode(QSGTexture::Repeat); + tx->setVerticalWrapMode(QSGTexture::Repeat); + break; + case QQuickPathGradient::ReflectSpread: + tx->setHorizontalWrapMode(QSGTexture::MirroredRepeat); + tx->setVerticalWrapMode(QSGTexture::MirroredRepeat); + break; + default: + qWarning("Unknown gradient spread mode %d", grad.spread); + break; + } + m_cache[grad] = tx; + } + return tx; +} + +#endif // QT_NO_OPENGL + +// ***** JS-based alternative for creating static paths, (mostly) without QObjects ***** + +class QQuickPathItemJSEngineData : public QV8Engine::Deletable +{ +public: + QQuickPathItemJSEngineData(QV4::ExecutionEngine *engine); + + QV4::PersistentValue pathProto; + QV4::PersistentValue strokeFillParamsProto; +}; + +V4_DEFINE_EXTENSION(QQuickPathItemJSEngineData, engineData) + +namespace QV4 { +namespace Heap { + +struct QQuickPathItemJSPathPrototype : Object { + void init() { Object::init(); } +}; + +struct QQuickPathItemJSPath : Object { + void init() { Object::init(); } + QQuickPathItemPathObject *obj; +}; + +struct QQuickPathItemJSStrokeFillParamsPrototype : Object { + void init() { Object::init(); } +}; + +struct QQuickPathItemJSStrokeFillParams : Object { + void init() { Object::init(); } + QQuickPathItemStrokeFillParamsObject *obj; +}; + +} // namespace Heap +} // namespace QV4 + +struct QQuickPathItemJSPathPrototype : public QV4::Object +{ + V4_OBJECT2(QQuickPathItemJSPathPrototype, QV4::Object) +public: + static QV4::Heap::QQuickPathItemJSPathPrototype *create(QV4::ExecutionEngine *engine) + { + QV4::Scope scope(engine); + auto obj = engine->memoryManager->allocObject(); + QV4::Scoped o(scope, obj); + + o->defineDefaultProperty(QStringLiteral("clear"), method_clear, 0); + o->defineDefaultProperty(QStringLiteral("moveTo"), method_moveTo, 0); + o->defineDefaultProperty(QStringLiteral("lineTo"), method_lineTo, 0); + o->defineDefaultProperty(QStringLiteral("quadTo"), method_quadTo, 0); + o->defineDefaultProperty(QStringLiteral("cubicTo"), method_cubicTo, 0); + o->defineDefaultProperty(QStringLiteral("arcTo"), method_arcTo, 0); + + return o->d(); + } + + static void method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_moveTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_lineTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_quadTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_cubicTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_arcTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); +}; + +DEFINE_OBJECT_VTABLE(QQuickPathItemJSPathPrototype); + +struct QQuickPathItemJSStrokeFillParamsPrototype : public QV4::Object +{ + V4_OBJECT2(QQuickPathItemJSStrokeFillParamsPrototype, QV4::Object) +public: + static QV4::Heap::QQuickPathItemJSStrokeFillParamsPrototype *create(QV4::ExecutionEngine *engine) + { + QV4::Scope scope(engine); + auto obj = engine->memoryManager->allocObject(); + QV4::Scoped o(scope, obj); + + o->defineDefaultProperty(QStringLiteral("clear"), method_clear, 0); + + return o->d(); + } + + static void method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); +}; + +DEFINE_OBJECT_VTABLE(QQuickPathItemJSStrokeFillParamsPrototype); + +struct QQuickPathItemJSPath : public QV4::Object +{ + V4_OBJECT2(QQuickPathItemJSPath, QV4::Object) +}; + +DEFINE_OBJECT_VTABLE(QQuickPathItemJSPath); + +struct QQuickPathItemJSStrokeFillParams : public QV4::Object +{ + V4_OBJECT2(QQuickPathItemJSStrokeFillParams, QV4::Object) + + static void method_get_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); +}; + +DEFINE_OBJECT_VTABLE(QQuickPathItemJSStrokeFillParams); + +void QQuickPathItemJSPathPrototype::method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + r->d()->obj->clear(); + + scope.result = callData->thisObject.asReturnedValue(); +} + +void QQuickPathItemJSPathPrototype::method_moveTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + if (callData->argc >= 2) { + QQuickPathItemPathObject *p = r->d()->obj; + p->path.cmd.append(QQuickPathItemPath::MoveTo); + p->path.coords.append(callData->args[0].toNumber()); + p->path.coords.append(callData->args[1].toNumber()); + } + + scope.result = callData->thisObject.asReturnedValue(); +} + +void QQuickPathItemJSPathPrototype::method_lineTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + if (callData->argc >= 2) { + QQuickPathItemPathObject *p = r->d()->obj; + p->path.cmd.append(QQuickPathItemPath::LineTo); + p->path.coords.append(callData->args[0].toNumber()); + p->path.coords.append(callData->args[1].toNumber()); + } + + scope.result = callData->thisObject.asReturnedValue(); +} + +void QQuickPathItemJSPathPrototype::method_quadTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + if (callData->argc >= 4) { + QQuickPathItemPathObject *p = r->d()->obj; + p->path.cmd.append(QQuickPathItemPath::QuadTo); + const QV4::Value *v = callData->args; + p->path.coords.append(v[0].toNumber()); // cx + p->path.coords.append(v[1].toNumber()); // cy + p->path.coords.append(v[2].toNumber()); // x + p->path.coords.append(v[3].toNumber()); // y + } + + scope.result = callData->thisObject.asReturnedValue(); +} + +void QQuickPathItemJSPathPrototype::method_cubicTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + if (callData->argc >= 6) { + QQuickPathItemPathObject *p = r->d()->obj; + p->path.cmd.append(QQuickPathItemPath::CubicTo); + const QV4::Value *v = callData->args; + p->path.coords.append(v[0].toNumber()); // c1x + p->path.coords.append(v[1].toNumber()); // c1y + p->path.coords.append(v[2].toNumber()); // c2x + p->path.coords.append(v[3].toNumber()); // c2y + p->path.coords.append(v[4].toNumber()); // x + p->path.coords.append(v[5].toNumber()); // y + } + + scope.result = callData->thisObject.asReturnedValue(); +} + +void QQuickPathItemJSPathPrototype::method_arcTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + if (callData->argc >= 7) { + QQuickPathItemPathObject *p = r->d()->obj; + p->path.cmd.append(QQuickPathItemPath::ArcTo); + const QV4::Value *v = callData->args; + p->path.coords.append(v[0].toNumber()); // radiusX + p->path.coords.append(v[1].toNumber()); // radiusY + p->path.coords.append(v[2].toNumber()); // xAxisRotation + p->path.coords.append(v[3].toNumber()); // x + p->path.coords.append(v[4].toNumber()); // y + p->path.coords.append(v[5].toNumber()); // sweepFlag + p->path.coords.append(v[6].toNumber()); // largeArc + } + + scope.result = callData->thisObject.asReturnedValue(); +} + +void QQuickPathItemJSStrokeFillParamsPrototype::method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + r->d()->obj->clear(); + + scope.result = callData->thisObject.asReturnedValue(); +} + +Q_QUICK_PRIVATE_EXPORT QColor qt_color_from_string(const QV4::Value &name); // qquickcontext2d.cpp + +static inline QString qt_color_string(const QColor &color) +{ + if (color.alpha() == 255) + return color.name(); + QString alphaString = QString::number(color.alphaF(), 'f'); + while (alphaString.endsWith(QLatin1Char('0'))) + alphaString.chop(1); + if (alphaString.endsWith(QLatin1Char('.'))) + alphaString += QLatin1Char('0'); + return QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString); +} + +void QQuickPathItemJSStrokeFillParams::method_get_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = QV4::Encode(scope.engine->newString(qt_color_string(r->d()->obj->sfp.strokeColor))); +} + +void QQuickPathItemJSStrokeFillParams::method_set_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isString()) + r->d()->obj->sfp.strokeColor = qt_color_from_string(value); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.strokeWidth); +} + +void QQuickPathItemJSStrokeFillParams::method_set_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + r->d()->obj->sfp.strokeWidth = value->toNumber(); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = QV4::Encode(scope.engine->newString(qt_color_string(r->d()->obj->sfp.fillColor))); +} + +void QQuickPathItemJSStrokeFillParams::method_set_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isString()) + r->d()->obj->sfp.fillColor = qt_color_from_string(value); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.fillRule); +} + +void QQuickPathItemJSStrokeFillParams::method_set_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isInt32()) + r->d()->obj->sfp.fillRule = QQuickVisualPath::FillRule(value->integerValue()); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.joinStyle); +} + +void QQuickPathItemJSStrokeFillParams::method_set_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isInt32()) + r->d()->obj->sfp.joinStyle = QQuickVisualPath::JoinStyle(value->integerValue()); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.miterLimit); +} + +void QQuickPathItemJSStrokeFillParams::method_set_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + r->d()->obj->sfp.miterLimit = value->toNumber(); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.capStyle); +} + +void QQuickPathItemJSStrokeFillParams::method_set_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isInt32()) + r->d()->obj->sfp.capStyle = QQuickVisualPath::CapStyle(value->integerValue()); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.strokeStyle); +} + +void QQuickPathItemJSStrokeFillParams::method_set_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isInt32()) + r->d()->obj->sfp.strokeStyle = QQuickVisualPath::StrokeStyle(value->integerValue()); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.dashOffset); +} + +void QQuickPathItemJSStrokeFillParams::method_set_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + r->d()->obj->sfp.dashOffset = value->toNumber(); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedArrayObject a(scope, scope.engine->newArrayObject()); + QQuickPathItemStrokeFillParamsObject *p = r->d()->obj; + a->arrayReserve(p->sfp.dashPattern.count()); + QV4::ScopedValue v(scope); + for (int i = 0; i < p->sfp.dashPattern.count(); ++i) + a->arrayPut(i, (v = scope.engine->fromVariant(p->sfp.dashPattern[i]))); + a->setArrayLengthUnchecked(p->sfp.dashPattern.count()); + + scope.result = a.asReturnedValue(); +} + +void QQuickPathItemJSStrokeFillParams::method_set_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isObject()) { + QV4::Scoped ao(scope, value); + if (!!ao) { + QQuickPathItemStrokeFillParamsObject *p = r->d()->obj; + p->sfp.dashPattern.resize(ao->getLength()); + QV4::ScopedValue val(scope); + for (int i = 0; i < p->sfp.dashPattern.count(); ++i) { + val = ao->getIndexed(i); + p->sfp.dashPattern[i] = val->toNumber(); + } + } + } + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = r->d()->obj->v4fillGradient.value(); +} + +void QQuickPathItemJSStrokeFillParams::method_set_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + QV4::Scoped qobjectWrapper(scope, value); + if (!!qobjectWrapper) { + if (QQuickPathGradient *grad = qobject_cast(qobjectWrapper->object())) { + r->d()->obj->v4fillGradient.set(scope.engine, value); + r->d()->obj->sfp.fillGradient = grad; + } + } else { + r->d()->obj->v4fillGradient.set(scope.engine, nullptr); + r->d()->obj->sfp.fillGradient = nullptr; + } + + scope.result = QV4::Encode::undefined(); +} + +QQuickPathItemJSEngineData::QQuickPathItemJSEngineData(QV4::ExecutionEngine *v4) +{ + QV4::Scope scope(v4); + + QV4::ScopedObject proto(scope, QQuickPathItemJSPathPrototype::create(v4)); + pathProto = proto; + + proto = QV4::ScopedObject(scope, QQuickPathItemJSStrokeFillParamsPrototype::create(v4)); + + proto->defineAccessorProperty(QStringLiteral("strokeColor"), + QQuickPathItemJSStrokeFillParams::method_get_strokeColor, + QQuickPathItemJSStrokeFillParams::method_set_strokeColor); + proto->defineAccessorProperty(QStringLiteral("strokeWidth"), + QQuickPathItemJSStrokeFillParams::method_get_strokeWidth, + QQuickPathItemJSStrokeFillParams::method_set_strokeWidth); + proto->defineAccessorProperty(QStringLiteral("fillColor"), + QQuickPathItemJSStrokeFillParams::method_get_fillColor, + QQuickPathItemJSStrokeFillParams::method_set_fillColor); + proto->defineAccessorProperty(QStringLiteral("fillRule"), + QQuickPathItemJSStrokeFillParams::method_get_fillRule, + QQuickPathItemJSStrokeFillParams::method_set_fillRule); + proto->defineAccessorProperty(QStringLiteral("joinStyle"), + QQuickPathItemJSStrokeFillParams::method_get_joinStyle, + QQuickPathItemJSStrokeFillParams::method_set_joinStyle); + proto->defineAccessorProperty(QStringLiteral("miterLimit"), + QQuickPathItemJSStrokeFillParams::method_get_miterLimit, + QQuickPathItemJSStrokeFillParams::method_set_miterLimit); + proto->defineAccessorProperty(QStringLiteral("capStyle"), + QQuickPathItemJSStrokeFillParams::method_get_capStyle, + QQuickPathItemJSStrokeFillParams::method_set_capStyle); + proto->defineAccessorProperty(QStringLiteral("strokeStyle"), + QQuickPathItemJSStrokeFillParams::method_get_strokeStyle, + QQuickPathItemJSStrokeFillParams::method_set_strokeStyle); + proto->defineAccessorProperty(QStringLiteral("dashOffset"), + QQuickPathItemJSStrokeFillParams::method_get_dashOffset, + QQuickPathItemJSStrokeFillParams::method_set_dashOffset); + proto->defineAccessorProperty(QStringLiteral("dashPattern"), + QQuickPathItemJSStrokeFillParams::method_get_dashPattern, + QQuickPathItemJSStrokeFillParams::method_set_dashPattern); + proto->defineAccessorProperty(QStringLiteral("fillGradient"), + QQuickPathItemJSStrokeFillParams::method_get_fillGradient, + QQuickPathItemJSStrokeFillParams::method_set_fillGradient); + + strokeFillParamsProto = proto; +} + +void QQuickPathItemPathObject::setV4Engine(QV4::ExecutionEngine *engine) +{ + QQuickPathItemJSEngineData *ed = engineData(engine); + QV4::Scope scope(engine); + QV4::Scoped wrapper(scope, engine->memoryManager->allocObject()); + QV4::ScopedObject p(scope, ed->pathProto.value()); + wrapper->setPrototype(p); + wrapper->d()->obj = this; + m_v4value = wrapper; +} + +/*! + \qmltype JSPath + \inqmlmodule QtQuick + \ingroup qtquick-path + \brief Describes a path via a JavaScript API + */ + +/*! + \qmlmethod void QtQuick::JSPath::moveTo(x, y) + + Moves the path's position to the absolute position specified by (\a x, \a y). + */ + +/*! + \qmlmethod void QtQuick::JSPath::lineTo(x, y) + + Defines a straight line to the absolute position specified by (\a x, \a y). + */ + +/*! + \qmlmethod void QtQuick::JSPath::quadTo(cx, cy, x, y) + + Defines a quadratic Bezier curve with a control point (\a cx, \a cy) and an + end point of (\a x, \a y). + */ + +/*! + \qmlmethod void QtQuick::JSPath::cubicTo(c1x, c1y, c2x, c2y, x, y) + + Defines a cubic Bezier curve with two control points (\a c1x, \a c1y) and + (\a c2x, \a c2y), and an end point of (\a x, \a y). + */ + +/*! + \qmlmethod void QtQuick::JSPath::arcTo(radiusX, radiusY, xAxisRotation, x, y, sweepFlag, largeArc) + + Defines an elliptical arc, following the elliptical arc command in SVG. See + \l{https://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands}{the + SVG path specification} for details on the parameters. + */ + +/*! + \qmlmethod void QtQuick::JSPath::clear() + + Clears the path object removing all path elements. This is more lightweight + than creating a new JSPath object. + */ + +void QQuickPathItemPathObject::clear() +{ + path = QQuickPathItemPath(); +} + +/*! + \qmltype StrokeFillParams + \inqmlmodule QtQuick + \ingroup qtquick-path + \brief Describes stroke and fill parameters via a JavaScript API + + The properties of StrokeFillParams objects correspond 1:1 to VisualPath + properties. The possible values for enumerations are the same as well, for + example: + + \code + sfp.strokeStyle = VisualPath.DashLine; + sfp.capStyle = VisualPath.RoundCap; + \endcode + */ + +/*! + \qmlproperty color QtQuick::StrokeFillParams::strokeColor + */ + +/*! + \qmlproperty real QtQuick::StrokeFillParams::strokeWidth + */ + +/*! + \qmlproperty color QtQuick::StrokeFillParams::fillColor + */ + +/*! + \qmlproperty enumeration QtQuick::StrokeFillParams::fillRule + */ + +/*! + \qmlproperty enumeration QtQuick::StrokeFillParams::joinStyle + */ + +/*! + \qmlproperty int QtQuick::StrokeFillParams::miterLimit + */ + +/*! + \qmlproperty enumeration QtQuick::StrokeFillParams::capStyle + */ + +/*! + \qmlproperty enumeration QtQuick::StrokeFillParams::strokeStyle + */ + +/*! + \qmlproperty real QtQuick::StrokeFillParams::dashOffset + */ + +/*! + \qmlproperty list QtQuick::StrokeFillParams::dashPattern + + The dash pattern can be specified using JavaScript arrays. + + \code + sfp.dashPattern = [ 4, 2 ]; + \endcode + */ + +/*! + \qmlproperty object QtQuick::StrokeFillParams::fillGradient + + Sets the fill gradient. The default value is null. Gradients cannot be + created from JavaScript. Instead, reference a PathLinearGradient or other + item by id. + + \code + PathLinearGradient { id: grad; ... } + ... + sfp.fillGradient = grad; + \endcode + */ + +/*! + \qmlmethod void QtQuick::StrokeFillParams::clear() + + Resets all values to their defaults. This is more lightweight than creating + a new StrokeFillParams object. + */ + +void QQuickPathItemStrokeFillParamsObject::clear() +{ + sfp = QQuickPathItemStrokeFillParams(); + if (!v4fillGradient.isNullOrUndefined()) + v4fillGradient.set(v4fillGradient.engine(), nullptr); +} + +void QQuickPathItemStrokeFillParamsObject::setV4Engine(QV4::ExecutionEngine *engine) +{ + QQuickPathItemJSEngineData *ed = engineData(engine); + QV4::Scope scope(engine); + QV4::Scoped wrapper(scope, engine->memoryManager->allocObject()); + QV4::ScopedObject p(scope, ed->strokeFillParamsProto.value()); + wrapper->setPrototype(p); + wrapper->d()->obj = this; + m_v4value = wrapper; +} + +/*! + \qmlmethod JSPath QtQuick::PathItem::newPath() + + Creates and returns a new object that describes a path and offers a + JavaScript API. Paired with a stroke-fill parameter object it is + equivalent to a VisualPath. + + The following two snippets are equivalent when it comes to the end result: + + \code + var p = pathItem.newPath(); + var sfp = pathItem.newStrokeFillParams(); + sfp.fillColor = "white"; + sfp.strokeColor = "black"; + sfp.strokeWidth = 0.172; + p.moveTo(-122.304, 84.285); + p.cubicTo(-122.304, 84.285, -122.203, 86.179, -123.027, 86.16); + pathItem.appendVisualPath(p, sfp); + \endcode + + \code + PathItem { + VisualPath { + fillColor: "white" + strokeColor: "black" + strokeWidth: 0.172 + Path { + startX: -122.304; startY: 84.285 + PathCubic { control1X: -122.304; control1Y: 84.285; control2X: -122.203; control2Y: 86.179; x: -123.027; y: 86.16 } + } + } + } + \endcode + + The latter offers a full declarative API, with the possibility to binding + to and animating properties, while the former uses less resources due to + greatly reducing the number of QObject instances created. +*/ + +void QQuickPathItem::newPath(QQmlV4Function *args) +{ + QQuickPathItemPathObject *obj = new QQuickPathItemPathObject(this); + obj->setV4Engine(QQmlEnginePrivate::get(qmlEngine(this))->v4engine()); + args->setReturnValue(obj->v4value()); +} + +/*! + \qmlmethod StrokeFillParams QtQuick::PathItem::newStrokeFillParams() + + Creates and returns a new object that describes stroke and fill parameters + and offers a JavaScript API. Paired with a path object it is equivalent to + a VisualPath. + */ + +void QQuickPathItem::newStrokeFillParams(QQmlV4Function *args) +{ + QQuickPathItemStrokeFillParamsObject *obj = new QQuickPathItemStrokeFillParamsObject(this); + obj->setV4Engine(QQmlEnginePrivate::get(qmlEngine(this))->v4engine()); + args->setReturnValue(obj->v4value()); +} + +/*! + \qmlmethod void QtQuick::PathItem::clearVisualPaths() + + Clears the list of visual paths. + + \note This applies only to path and stroke-fill parameter objects registered + via appendVisualPaths(). + */ + +void QQuickPathItem::clearVisualPaths(QQmlV4Function *args) +{ + Q_UNUSED(args); + Q_D(QQuickPathItem); + d->jsData.paths.clear(); + d->jsData.sfp.clear(); +} + +/*! + \qmlmethod void QtQuick::PathItem::commitVisualPaths() + + Updates the PathItem. + + In order to avoid rendering a half-prepared PathItem, calling + PathItem.appendVisualPath() does not trigger any processing. Instead, + applications must call this function when all path and stroke-fill + parameter objects are registered. + */ + +void QQuickPathItem::commitVisualPaths(QQmlV4Function *args) +{ + Q_UNUSED(args); + Q_D(QQuickPathItem); + d->_q_visualPathChanged(); +} + +/*! + \qmlmethod void QtQuick::PathItem::appendVisualPath(object path, object strokeFillParams) + + Adds the visual path compoes of \a path and \a strokeFillParams into the + PathItem. + + \note The declarative and imprative (JavaScript) APIs of PathItem use + independent data structures. Calling this function has no effect on the + PathItem.elements property and vice versa. Once this function is called, + the PathItem will only consider the data registered via this function and + will ignore the declarative elements property. + */ + +void QQuickPathItem::appendVisualPath(QQmlV4Function *args) +{ + if (args->length() < 2) + return; + + Q_D(QQuickPathItem); + QV4::Scope scope(args->v4engine()); + QV4::Scoped jsp(scope, (*args)[0]); + QV4::Scoped jssfp(scope, (*args)[1]); + + const QQuickPathItemPath &path(jsp->d()->obj->path); + const QQuickPathItemStrokeFillParams &sfp(jssfp->d()->obj->sfp); + + d->jsData.paths.append(path); + d->jsData.sfp.append(sfp); +} + +QT_END_NAMESPACE + +#include "moc_qquickpathitem_p.cpp" diff --git a/src/imports/pathitem/qquickpathitem_p.h b/src/imports/pathitem/qquickpathitem_p.h new file mode 100644 index 0000000000..c7c56fd5d8 --- /dev/null +++ b/src/imports/pathitem/qquickpathitem_p.h @@ -0,0 +1,333 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPATHITEM_P_H +#define QQUICKPATHITEM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickitem.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickVisualPathPrivate; +class QQuickPathItemPrivate; + +class QQuickPathGradientStop : public QObject +{ + Q_OBJECT + Q_PROPERTY(qreal position READ position WRITE setPosition) + Q_PROPERTY(QColor color READ color WRITE setColor) + +public: + QQuickPathGradientStop(QObject *parent = nullptr); + + qreal position() const; + void setPosition(qreal position); + + QColor color() const; + void setColor(const QColor &color); + +private: + qreal m_position; + QColor m_color; +}; + +class QQuickPathGradient : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQmlListProperty stops READ stops) + Q_PROPERTY(SpreadMode spread READ spread WRITE setSpread NOTIFY spreadChanged) + Q_CLASSINFO("DefaultProperty", "stops") + +public: + enum SpreadMode { + PadSpread, + RepeatSpread, + ReflectSpread + }; + Q_ENUM(SpreadMode) + + QQuickPathGradient(QObject *parent = nullptr); + + QQmlListProperty stops(); + + QGradientStops sortedGradientStops() const; + + SpreadMode spread() const; + void setSpread(SpreadMode mode); + +signals: + void updated(); + void spreadChanged(); + +private: + static int countStops(QQmlListProperty *list); + static QObject *atStop(QQmlListProperty *list, int index); + static void appendStop(QQmlListProperty *list, QObject *stop); + + QVector m_stops; + SpreadMode m_spread; +}; + +class QQuickPathLinearGradient : public QQuickPathGradient +{ + Q_OBJECT + Q_PROPERTY(qreal x1 READ x1 WRITE setX1 NOTIFY x1Changed) + Q_PROPERTY(qreal y1 READ y1 WRITE setY1 NOTIFY y1Changed) + Q_PROPERTY(qreal x2 READ x2 WRITE setX2 NOTIFY x2Changed) + Q_PROPERTY(qreal y2 READ y2 WRITE setY2 NOTIFY y2Changed) + Q_CLASSINFO("DefaultProperty", "stops") + +public: + QQuickPathLinearGradient(QObject *parent = nullptr); + + qreal x1() const; + void setX1(qreal v); + qreal y1() const; + void setY1(qreal v); + qreal x2() const; + void setX2(qreal v); + qreal y2() const; + void setY2(qreal v); + +signals: + void x1Changed(); + void y1Changed(); + void x2Changed(); + void y2Changed(); + +private: + QPointF m_start; + QPointF m_end; +}; + +class QQuickVisualPath : public QObject +{ + Q_OBJECT + + 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) + Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged) + Q_PROPERTY(FillRule fillRule READ fillRule WRITE setFillRule NOTIFY fillRuleChanged) + Q_PROPERTY(JoinStyle joinStyle READ joinStyle WRITE setJoinStyle NOTIFY joinStyleChanged) + Q_PROPERTY(int miterLimit READ miterLimit WRITE setMiterLimit NOTIFY miterLimitChanged) + Q_PROPERTY(CapStyle capStyle READ capStyle WRITE setCapStyle NOTIFY capStyleChanged) + Q_PROPERTY(StrokeStyle strokeStyle READ strokeStyle WRITE setStrokeStyle NOTIFY strokeStyleChanged) + Q_PROPERTY(qreal dashOffset READ dashOffset WRITE setDashOffset NOTIFY dashOffsetChanged) + Q_PROPERTY(QVector dashPattern READ dashPattern WRITE setDashPattern NOTIFY dashPatternChanged) + Q_PROPERTY(QQuickPathGradient *fillGradient READ fillGradient WRITE setFillGradient RESET resetFillGradient) + +public: + enum FillRule { + OddEvenFill = Qt::OddEvenFill, + WindingFill = Qt::WindingFill + }; + Q_ENUM(FillRule) + + enum JoinStyle { + MiterJoin = Qt::MiterJoin, + BevelJoin = Qt::BevelJoin, + RoundJoin = Qt::RoundJoin + }; + Q_ENUM(JoinStyle) + + enum CapStyle { + FlatCap = Qt::FlatCap, + SquareCap = Qt::SquareCap, + RoundCap = Qt::RoundCap + }; + Q_ENUM(CapStyle) + + enum StrokeStyle { + SolidLine = Qt::SolidLine, + DashLine = Qt::DashLine + }; + Q_ENUM(StrokeStyle) + + QQuickVisualPath(QObject *parent = nullptr); + ~QQuickVisualPath(); + + QQuickPath *path() const; + void setPath(QQuickPath *path); + + QColor strokeColor() const; + void setStrokeColor(const QColor &color); + + qreal strokeWidth() const; + void setStrokeWidth(qreal w); + + QColor fillColor() const; + void setFillColor(const QColor &color); + + FillRule fillRule() const; + void setFillRule(FillRule fillRule); + + JoinStyle joinStyle() const; + void setJoinStyle(JoinStyle style); + + int miterLimit() const; + void setMiterLimit(int limit); + + CapStyle capStyle() const; + void setCapStyle(CapStyle style); + + StrokeStyle strokeStyle() const; + void setStrokeStyle(StrokeStyle style); + + qreal dashOffset() const; + void setDashOffset(qreal offset); + + QVector dashPattern() const; + void setDashPattern(const QVector &array); + + QQuickPathGradient *fillGradient() const; + void setFillGradient(QQuickPathGradient *gradient); + void resetFillGradient(); + +Q_SIGNALS: + void changed(); + void pathChanged(); + void strokeColorChanged(); + void strokeWidthChanged(); + void fillColorChanged(); + void fillRuleChanged(); + void joinStyleChanged(); + void miterLimitChanged(); + void capStyleChanged(); + void strokeStyleChanged(); + void dashOffsetChanged(); + void dashPatternChanged(); + void fillGradientChanged(); + +private: + Q_DISABLE_COPY(QQuickVisualPath) + Q_DECLARE_PRIVATE(QQuickVisualPath) + Q_PRIVATE_SLOT(d_func(), void _q_pathChanged()) + Q_PRIVATE_SLOT(d_func(), void _q_fillGradientChanged()) +}; + +class QQuickPathItem : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged) + Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) + Q_PROPERTY(bool enableVendorExtensions READ enableVendorExtensions WRITE setEnableVendorExtensions NOTIFY enableVendorExtensionsChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(QQmlListProperty elements READ elements) + Q_CLASSINFO("DefaultProperty", "elements") + +public: + enum RendererType { + UnknownRenderer, + GeometryRenderer, + NvprRenderer, + SoftwareRenderer + }; + Q_ENUM(RendererType) + + enum Status { + Null, + Ready, + Processing + }; + Q_ENUM(Status) + + QQuickPathItem(QQuickItem *parent = nullptr); + ~QQuickPathItem(); + + RendererType rendererType() const; + + bool asynchronous() const; + void setAsynchronous(bool async); + + bool enableVendorExtensions() const; + void setEnableVendorExtensions(bool enable); + + Status status() const; + + QQmlListProperty elements(); + + Q_INVOKABLE void newPath(QQmlV4Function *args); + Q_INVOKABLE void newStrokeFillParams(QQmlV4Function *args); + Q_INVOKABLE void clearVisualPaths(QQmlV4Function *args); + Q_INVOKABLE void commitVisualPaths(QQmlV4Function *args); + Q_INVOKABLE void appendVisualPath(QQmlV4Function *args); + +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(); + void asynchronousChanged(); + void enableVendorExtensionsChanged(); + void statusChanged(); + +private: + Q_DISABLE_COPY(QQuickPathItem) + Q_DECLARE_PRIVATE(QQuickPathItem) + Q_PRIVATE_SLOT(d_func(), void _q_visualPathChanged()) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPathItem) + +#endif // QQUICKPATHITEM_P_H diff --git a/src/imports/pathitem/qquickpathitem_p_p.h b/src/imports/pathitem/qquickpathitem_p_p.h new file mode 100644 index 0000000000..6dde314a30 --- /dev/null +++ b/src/imports/pathitem/qquickpathitem_p_p.h @@ -0,0 +1,282 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPATHITEM_P_P_H +#define QQUICKPATHITEM_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickpathitem_p.h" +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QSGPlainTexture; + +struct QQuickPathItemPath +{ + enum Command { + MoveTo, + LineTo, + QuadTo, + CubicTo, + ArcTo + }; + + QVector cmd; + QVector coords; + + QPainterPath toPainterPath() const; +}; + +struct QQuickPathItemStrokeFillParams +{ + QQuickPathItemStrokeFillParams(); + + QColor strokeColor; + qreal strokeWidth; + QColor fillColor; + QQuickVisualPath::FillRule fillRule; + QQuickVisualPath::JoinStyle joinStyle; + int miterLimit; + QQuickVisualPath::CapStyle capStyle; + QQuickVisualPath::StrokeStyle strokeStyle; + qreal dashOffset; + QVector dashPattern; + QQuickPathGradient *fillGradient; +}; + +class QQuickAbstractPathRenderer +{ +public: + enum Flag { + SupportsAsync = 0x01 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + virtual ~QQuickAbstractPathRenderer() { } + + // Gui thread + virtual void beginSync(int totalCount) = 0; + virtual void endSync(bool async) = 0; + virtual void setAsyncCallback(void (*)(void *), void *) { } + virtual Flags flags() const { return 0; } + // - QML API + virtual void setPath(int index, const QQuickPath *path) = 0; + // - JS API + virtual void setJSPath(int index, const QQuickPathItemPath &path) = 0; + // - stroke/fill parameters + 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 &dashPattern) = 0; + virtual void setFillGradient(int index, QQuickPathGradient *gradient) = 0; + + // Render thread, with gui blocked + virtual void updateNode() = 0; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickAbstractPathRenderer::Flags) + +class QQuickVisualPathPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QQuickVisualPath) + +public: + enum Dirty { + DirtyPath = 0x01, + DirtyStrokeColor = 0x02, + DirtyStrokeWidth = 0x04, + DirtyFillColor = 0x08, + DirtyFillRule = 0x10, + DirtyStyle = 0x20, + DirtyDash = 0x40, + DirtyFillGradient = 0x80, + + DirtyAll = 0xFF + }; + + QQuickVisualPathPrivate(); + + void _q_pathChanged(); + void _q_fillGradientChanged(); + + static QQuickVisualPathPrivate *get(QQuickVisualPath *p) { return p->d_func(); } + + QQuickPath *path; + int dirty; + QQuickPathItemStrokeFillParams sfp; +}; + +class QQuickPathItemPrivate : public QQuickItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickPathItem) + +public: + QQuickPathItemPrivate(); + ~QQuickPathItemPrivate(); + + void createRenderer(); + QSGNode *createNode(); + void sync(); + + void _q_visualPathChanged(); + void setStatus(QQuickPathItem::Status newStatus); + + static QQuickPathItemPrivate *get(QQuickPathItem *item) { return item->d_func(); } + + bool componentComplete; + bool vpChanged; + QQuickPathItem::RendererType rendererType; + bool async; + QQuickPathItem::Status status; + QQuickAbstractPathRenderer *renderer; + + struct { + QVector vp; + } qmlData; + + struct { + bool isValid() const { Q_ASSERT(paths.count() == sfp.count()); return !paths.isEmpty(); } + QVector paths; + QVector sfp; + } jsData; + + bool enableVendorExts; +}; + +class QQuickPathItemPathObject : public QObject +{ + Q_OBJECT + +public: + QQuickPathItemPathObject(QObject *parent = nullptr) : QObject(parent) { } + + void setV4Engine(QV4::ExecutionEngine *engine); + QV4::ReturnedValue v4value() const { return m_v4value.value(); } + + QQuickPathItemPath path; + + void clear(); + +private: + QV4::PersistentValue m_v4value; +}; + +class QQuickPathItemStrokeFillParamsObject : public QObject +{ + Q_OBJECT + +public: + QQuickPathItemStrokeFillParamsObject(QObject *parent = nullptr) : QObject(parent) { } + + void setV4Engine(QV4::ExecutionEngine *engine); + QV4::ReturnedValue v4value() const { return m_v4value.value(); } + + QQuickPathItemStrokeFillParams sfp; + QV4::PersistentValue v4fillGradient; + + void clear(); + +private: + QV4::PersistentValue m_v4value; +}; + +#ifndef QT_NO_OPENGL + +class QQuickPathItemGradientCache : public QOpenGLSharedResource +{ +public: + struct GradientDesc { + QGradientStops stops; + QPointF start; + QPointF end; + QQuickPathGradient::SpreadMode spread; + bool operator==(const GradientDesc &other) const + { + return start == other.start && end == other.end && spread == other.spread + && stops == other.stops; + } + }; + + QQuickPathItemGradientCache(QOpenGLContext *context) : QOpenGLSharedResource(context->shareGroup()) { } + ~QQuickPathItemGradientCache(); + + void invalidateResource() override; + void freeResource(QOpenGLContext *) override; + + QSGTexture *get(const GradientDesc &grad); + + static QQuickPathItemGradientCache *currentCache(); + +private: + QHash m_cache; +}; + +inline uint qHash(const QQuickPathItemGradientCache::GradientDesc &v, uint seed = 0) +{ + uint h = seed; + h += v.start.x() + v.end.y() + v.spread; + for (int i = 0; i < 3 && i < v.stops.count(); ++i) + h += v.stops[i].second.rgba(); + return h; +} + +#endif // QT_NO_OPENGL + +QT_END_NAMESPACE + +#endif diff --git a/src/imports/pathitem/qquickpathitemgenericrenderer.cpp b/src/imports/pathitem/qquickpathitemgenericrenderer.cpp new file mode 100644 index 0000000000..4e8fe55df2 --- /dev/null +++ b/src/imports/pathitem/qquickpathitemgenericrenderer.cpp @@ -0,0 +1,775 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickpathitemgenericrenderer_p.h" +#include +#include +#include + +#ifndef QT_NO_OPENGL +#include +#include +#include +#include +#endif + +QT_BEGIN_NAMESPACE + +static const qreal TRI_SCALE = 1; + +struct ColoredVertex // must match QSGGeometry::ColoredPoint2D +{ + float x, y; + QQuickPathItemGenericRenderer::Color4ub color; + void set(float nx, float ny, QQuickPathItemGenericRenderer::Color4ub ncolor) + { + x = nx; y = ny; color = ncolor; + } +}; + +static inline QQuickPathItemGenericRenderer::Color4ub colorToColor4ub(const QColor &c) +{ + QQuickPathItemGenericRenderer::Color4ub color = { + uchar(qRound(c.redF() * c.alphaF() * 255)), + uchar(qRound(c.greenF() * c.alphaF() * 255)), + uchar(qRound(c.blueF() * c.alphaF() * 255)), + uchar(qRound(c.alphaF() * 255)) + }; + return color; +} + +QQuickPathItemGenericStrokeFillNode::QQuickPathItemGenericStrokeFillNode(QQuickWindow *window) + : m_geometry(new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0, 0)), + m_window(window), + m_material(nullptr) +{ + setGeometry(m_geometry); + activateMaterial(MatSolidColor); +#ifdef QSG_RUNTIME_DESCRIPTION + qsgnode_set_description(this, QLatin1String("stroke-fill")); +#endif +} + +QQuickPathItemGenericStrokeFillNode::~QQuickPathItemGenericStrokeFillNode() +{ + delete m_geometry; +} + +void QQuickPathItemGenericStrokeFillNode::activateMaterial(Material m) +{ + switch (m) { + case MatSolidColor: + // Use vertexcolor material. Items with different colors remain batchable + // this way, at the expense of having to provide per-vertex color values. + if (!m_solidColorMaterial) + m_solidColorMaterial.reset(QQuickPathItemGenericMaterialFactory::createVertexColor(m_window)); + m_material = m_solidColorMaterial.data(); + break; + case MatLinearGradient: + if (!m_linearGradientMaterial) + m_linearGradientMaterial.reset(QQuickPathItemGenericMaterialFactory::createLinearGradient(m_window, this)); + m_material = m_linearGradientMaterial.data(); + break; + default: + qWarning("Unknown material %d", m); + return; + } + + if (material() != m_material) + setMaterial(m_material); +} + +static bool q_supportsElementIndexUint(QSGRendererInterface::GraphicsApi api) +{ + static bool elementIndexUint = true; +#ifndef QT_NO_OPENGL + if (api == QSGRendererInterface::OpenGL) { + static bool elementIndexUintChecked = false; + if (!elementIndexUintChecked) { + elementIndexUintChecked = true; + QOpenGLContext *context = QOpenGLContext::currentContext(); + QScopedPointer dummyContext; + QScopedPointer dummySurface; + bool ok = true; + if (!context) { + dummyContext.reset(new QOpenGLContext); + dummyContext->create(); + context = dummyContext.data(); + dummySurface.reset(new QOffscreenSurface); + dummySurface->setFormat(context->format()); + dummySurface->create(); + ok = context->makeCurrent(dummySurface.data()); + } + if (ok) { + elementIndexUint = static_cast(context->functions())->hasOpenGLExtension( + QOpenGLExtensions::ElementIndexUint); + } + } + } +#else + Q_UNUSED(api); +#endif + return elementIndexUint; +} + +QQuickPathItemGenericRenderer::~QQuickPathItemGenericRenderer() +{ + for (VisualPathData &d : m_vp) { + if (d.pendingFill) + d.pendingFill->orphaned = true; + if (d.pendingStroke) + d.pendingStroke->orphaned = true; + } +} + +// sync, and so triangulation too, happens on the gui thread +// - except when async is set, in which case triangulation is moved to worker threads + +void QQuickPathItemGenericRenderer::beginSync(int totalCount) +{ + if (m_vp.count() != totalCount) { + m_vp.resize(totalCount); + m_accDirty |= DirtyList; + } + for (VisualPathData &d : m_vp) + d.syncDirty = 0; +} + +void QQuickPathItemGenericRenderer::setPath(int index, const QQuickPath *path) +{ + VisualPathData &d(m_vp[index]); + d.path = path ? path->path() : QPainterPath(); + d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom; +} + +void QQuickPathItemGenericRenderer::setJSPath(int index, const QQuickPathItemPath &path) +{ + VisualPathData &d(m_vp[index]); + d.path = path.toPainterPath(); + d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom; +} + +void QQuickPathItemGenericRenderer::setStrokeColor(int index, const QColor &color) +{ + VisualPathData &d(m_vp[index]); + d.strokeColor = colorToColor4ub(color); + d.syncDirty |= DirtyColor; +} + +void QQuickPathItemGenericRenderer::setStrokeWidth(int index, qreal w) +{ + VisualPathData &d(m_vp[index]); + d.strokeWidth = w; + if (w >= 0.0f) + d.pen.setWidthF(w); + d.syncDirty |= DirtyStrokeGeom; +} + +void QQuickPathItemGenericRenderer::setFillColor(int index, const QColor &color) +{ + VisualPathData &d(m_vp[index]); + d.fillColor = colorToColor4ub(color); + d.syncDirty |= DirtyColor; +} + +void QQuickPathItemGenericRenderer::setFillRule(int index, QQuickVisualPath::FillRule fillRule) +{ + VisualPathData &d(m_vp[index]); + d.fillRule = Qt::FillRule(fillRule); + d.syncDirty |= DirtyFillGeom; +} + +void QQuickPathItemGenericRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) +{ + VisualPathData &d(m_vp[index]); + d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); + d.pen.setMiterLimit(miterLimit); + d.syncDirty |= DirtyStrokeGeom; +} + +void QQuickPathItemGenericRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) +{ + VisualPathData &d(m_vp[index]); + d.pen.setCapStyle(Qt::PenCapStyle(capStyle)); + d.syncDirty |= DirtyStrokeGeom; +} + +void QQuickPathItemGenericRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) +{ + VisualPathData &d(m_vp[index]); + d.pen.setStyle(Qt::PenStyle(strokeStyle)); + if (strokeStyle == QQuickVisualPath::DashLine) { + d.pen.setDashPattern(dashPattern); + d.pen.setDashOffset(dashOffset); + } + d.syncDirty |= DirtyStrokeGeom; +} + +void QQuickPathItemGenericRenderer::setFillGradient(int index, QQuickPathGradient *gradient) +{ + VisualPathData &d(m_vp[index]); + d.fillGradientActive = gradient != nullptr; + if (gradient) { + d.fillGradient.stops = gradient->sortedGradientStops(); + d.fillGradient.spread = gradient->spread(); + if (QQuickPathLinearGradient *g = qobject_cast(gradient)) { + d.fillGradient.start = QPointF(g->x1(), g->y1()); + d.fillGradient.end = QPointF(g->x2(), g->y2()); + } else { + Q_UNREACHABLE(); + } + } + d.syncDirty |= DirtyFillGradient; +} + +void QQuickPathItemFillRunnable::run() +{ + QQuickPathItemGenericRenderer::triangulateFill(path, fillColor, &fillVertices, &fillIndices, &indexType, supportsElementIndexUint); + emit done(this); +} + +void QQuickPathItemStrokeRunnable::run() +{ + QQuickPathItemGenericRenderer::triangulateStroke(path, pen, strokeColor, &strokeVertices, clipSize); + emit done(this); +} + +void QQuickPathItemGenericRenderer::setAsyncCallback(void (*callback)(void *), void *data) +{ + m_asyncCallback = callback; + m_asyncCallbackData = data; +} + +static QThreadPool *pathWorkThreadPool = nullptr; + +static void deletePathWorkThreadPool() +{ + delete pathWorkThreadPool; + pathWorkThreadPool = nullptr; +} + +void QQuickPathItemGenericRenderer::endSync(bool async) +{ + bool didKickOffAsync = false; + + for (int i = 0; i < m_vp.count(); ++i) { + VisualPathData &d(m_vp[i]); + 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; + } + + if (async && !pathWorkThreadPool) { + qAddPostRoutine(deletePathWorkThreadPool); + pathWorkThreadPool = new QThreadPool; + const int idealCount = QThread::idealThreadCount(); + pathWorkThreadPool->setMaxThreadCount(idealCount > 0 ? idealCount * 2 : 4); + } + + if ((d.syncDirty & DirtyFillGeom) && d.fillColor.a) { + d.path.setFillRule(d.fillRule); + if (m_api == QSGRendererInterface::Unknown) + m_api = m_item->window()->rendererInterface()->graphicsApi(); + if (async) { + QQuickPathItemFillRunnable *r = new QQuickPathItemFillRunnable; + r->setAutoDelete(false); + if (d.pendingFill) + d.pendingFill->orphaned = true; + d.pendingFill = r; + r->path = d.path; + r->fillColor = d.fillColor; + r->supportsElementIndexUint = q_supportsElementIndexUint(m_api); + // Unlikely in practice but in theory m_vp could be + // resized. Therefore, capture 'i' instead of 'd'. + QObject::connect(r, &QQuickPathItemFillRunnable::done, qApp, [this, i](QQuickPathItemFillRunnable *r) { + // Bail out when orphaned (meaning either another run was + // started after this one, or the renderer got destroyed). + if (!r->orphaned && i < m_vp.count()) { + VisualPathData &d(m_vp[i]); + d.fillVertices = r->fillVertices; + d.fillIndices = r->fillIndices; + d.indexType = r->indexType; + d.pendingFill = nullptr; + d.effectiveDirty |= DirtyFillGeom; + maybeUpdateAsyncItem(); + } + r->deleteLater(); + }); + didKickOffAsync = true; + pathWorkThreadPool->start(r); + } else { + triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices, &d.indexType, q_supportsElementIndexUint(m_api)); + } + } + + if ((d.syncDirty & DirtyStrokeGeom) && d.strokeWidth >= 0.0f && d.strokeColor.a) { + if (async) { + QQuickPathItemStrokeRunnable *r = new QQuickPathItemStrokeRunnable; + r->setAutoDelete(false); + if (d.pendingStroke) + d.pendingStroke->orphaned = true; + d.pendingStroke = r; + r->path = d.path; + r->pen = d.pen; + r->strokeColor = d.strokeColor; + r->clipSize = QSize(m_item->width(), m_item->height()); + QObject::connect(r, &QQuickPathItemStrokeRunnable::done, qApp, [this, i](QQuickPathItemStrokeRunnable *r) { + if (!r->orphaned && i < m_vp.count()) { + VisualPathData &d(m_vp[i]); + d.strokeVertices = r->strokeVertices; + d.pendingStroke = nullptr; + d.effectiveDirty |= DirtyStrokeGeom; + maybeUpdateAsyncItem(); + } + r->deleteLater(); + }); + didKickOffAsync = true; + pathWorkThreadPool->start(r); + } else { + triangulateStroke(d.path, d.pen, d.strokeColor, &d.strokeVertices, + QSize(m_item->width(), m_item->height())); + } + } + } + + if (!didKickOffAsync && async && m_asyncCallback) + m_asyncCallback(m_asyncCallbackData); +} + +void QQuickPathItemGenericRenderer::maybeUpdateAsyncItem() +{ + for (const VisualPathData &d : qAsConst(m_vp)) { + if (d.pendingFill || d.pendingStroke) + return; + } + m_accDirty |= DirtyFillGeom | DirtyStrokeGeom; + m_item->update(); + if (m_asyncCallback) + m_asyncCallback(m_asyncCallbackData); +} + +// the stroke/fill triangulation functions may be invoked either on the gui +// thread or some worker thread and must thus be self-contained. +void QQuickPathItemGenericRenderer::triangulateFill(const QPainterPath &path, + const Color4ub &fillColor, + VertexContainerType *fillVertices, + IndexContainerType *fillIndices, + QSGGeometry::Type *indexType, + bool supportsElementIndexUint) +{ + const QVectorPath &vp = qtVectorPathForPath(path); + + QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(TRI_SCALE, TRI_SCALE), 1, supportsElementIndexUint); + const int vertexCount = ts.vertices.count() / 2; // just a qreal vector with x,y hence the / 2 + fillVertices->resize(vertexCount); + ColoredVertex *vdst = reinterpret_cast(fillVertices->data()); + const qreal *vsrc = ts.vertices.constData(); + for (int i = 0; i < vertexCount; ++i) + vdst[i].set(vsrc[i * 2] / TRI_SCALE, vsrc[i * 2 + 1] / TRI_SCALE, fillColor); + + size_t indexByteSize; + if (ts.indices.type() == QVertexIndexVector::UnsignedShort) { + *indexType = QSGGeometry::UnsignedShortType; + // fillIndices is still QVector. Just resize to N/2 and pack + // the N quint16s into it. + fillIndices->resize(ts.indices.size() / 2); + indexByteSize = ts.indices.size() * sizeof(quint16); + } else { + *indexType = QSGGeometry::UnsignedIntType; + fillIndices->resize(ts.indices.size()); + indexByteSize = ts.indices.size() * sizeof(quint32); + } + memcpy(fillIndices->data(), ts.indices.data(), indexByteSize); +} + +void QQuickPathItemGenericRenderer::triangulateStroke(const QPainterPath &path, + const QPen &pen, + const Color4ub &strokeColor, + VertexContainerType *strokeVertices, + const QSize &clipSize) +{ + const QVectorPath &vp = qtVectorPathForPath(path); + const QRectF clip(QPointF(0, 0), clipSize); + const qreal inverseScale = 1.0 / TRI_SCALE; + + QTriangulatingStroker stroker; + stroker.setInvScale(inverseScale); + + if (pen.style() == Qt::SolidLine) { + stroker.process(vp, pen, clip, 0); + } else { + QDashedStrokeProcessor dashStroker; + dashStroker.setInvScale(inverseScale); + dashStroker.process(vp, pen, clip, 0); + QVectorPath dashStroke(dashStroker.points(), dashStroker.elementCount(), + dashStroker.elementTypes(), 0); + stroker.process(dashStroke, pen, clip, 0); + } + + if (!stroker.vertexCount()) { + strokeVertices->clear(); + return; + } + + const int vertexCount = stroker.vertexCount() / 2; // just a float vector with x,y hence the / 2 + strokeVertices->resize(vertexCount); + ColoredVertex *vdst = reinterpret_cast(strokeVertices->data()); + const float *vsrc = stroker.vertices(); + for (int i = 0; i < vertexCount; ++i) + vdst[i].set(vsrc[i * 2], vsrc[i * 2 + 1], strokeColor); +} + +void QQuickPathItemGenericRenderer::setRootNode(QQuickPathItemGenericNode *node) +{ + if (m_rootNode != node) { + m_rootNode = node; + m_accDirty |= DirtyList; + } +} + +// on the render thread with gui blocked +void QQuickPathItemGenericRenderer::updateNode() +{ + if (!m_rootNode || !m_accDirty) + return; + +// [ 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); + } + + QQuickPathItemGenericNode *node = *nodePtr; + + if (m_accDirty & DirtyList) + d.effectiveDirty |= DirtyFillGeom | DirtyStrokeGeom | DirtyColor | DirtyFillGradient; + + if (!d.effectiveDirty) { + prevNode = node; + nodePtr = &node->m_next; + 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 |= DirtyFillGeom; + } + + 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 |= DirtyStrokeGeom; + } + + updateFillNode(&d, node); + updateStrokeNode(&d, node); + + d.effectiveDirty = 0; + + prevNode = node; + nodePtr = &node->m_next; + } + + if (*nodePtr && prevNode) { + prevNode->removeChildNode(*nodePtr); + delete *nodePtr; + *nodePtr = nullptr; + } + + m_accDirty = 0; +} + +void QQuickPathItemGenericRenderer::updateShadowDataInNode(VisualPathData *d, QQuickPathItemGenericStrokeFillNode *n) +{ + if (d->fillGradientActive) { + if (d->effectiveDirty & DirtyFillGradient) + n->m_fillGradient = d->fillGradient; + } +} + +void QQuickPathItemGenericRenderer::updateFillNode(VisualPathData *d, QQuickPathItemGenericNode *node) +{ + if (!node->m_fillNode) + return; + if (!(d->effectiveDirty & (DirtyFillGeom | DirtyColor | DirtyFillGradient))) + return; + + // Make a copy of the data that will be accessed by the material on + // the render thread. This must be done even when we bail out below. + QQuickPathItemGenericStrokeFillNode *n = node->m_fillNode; + updateShadowDataInNode(d, n); + + QSGGeometry *g = n->m_geometry; + if (d->fillVertices.isEmpty()) { + if (g->vertexCount() || g->indexCount()) { + g->allocate(0, 0); + n->markDirty(QSGNode::DirtyGeometry); + } + return; + } + + if (d->fillGradientActive) { + n->activateMaterial(QQuickPathItemGenericStrokeFillNode::MatLinearGradient); + if (d->effectiveDirty & DirtyFillGradient) { + // 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 (!(d->effectiveDirty & DirtyFillGeom)) + return; + } + } else { + n->activateMaterial(QQuickPathItemGenericStrokeFillNode::MatSolidColor); + // fast path for updating only color values when no change in vertex positions + if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyFillGeom)) { + ColoredVertex *vdst = reinterpret_cast(g->vertexData()); + for (int i = 0; i < g->vertexCount(); ++i) + vdst[i].set(vdst[i].x, vdst[i].y, d->fillColor); + n->markDirty(QSGNode::DirtyGeometry); + return; + } + } + + const int indexCount = d->indexType == QSGGeometry::UnsignedShortType + ? d->fillIndices.count() * 2 : d->fillIndices.count(); + if (g->indexType() != d->indexType) { + g = new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), + d->fillVertices.count(), indexCount, d->indexType); + n->setGeometry(g); + delete n->m_geometry; + n->m_geometry = g; + } else { + g->allocate(d->fillVertices.count(), indexCount); + } + g->setDrawingMode(QSGGeometry::DrawTriangles); + 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(VisualPathData *d, QQuickPathItemGenericNode *node) +{ + if (!node->m_strokeNode) + return; + if (!(d->effectiveDirty & (DirtyStrokeGeom | DirtyColor))) + return; + + QQuickPathItemGenericStrokeFillNode *n = node->m_strokeNode; + QSGGeometry *g = n->m_geometry; + if (d->strokeVertices.isEmpty()) { + if (g->vertexCount() || g->indexCount()) { + g->allocate(0, 0); + n->markDirty(QSGNode::DirtyGeometry); + } + return; + } + + n->markDirty(QSGNode::DirtyGeometry); + + // Async loading runs update once, bails out above, then updates again once + // ready. Set the material dirty then. This is in-line with fill where the + // first activateMaterial() achieves the same. + if (!g->vertexCount()) + n->markDirty(QSGNode::DirtyMaterial); + + if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyStrokeGeom)) { + ColoredVertex *vdst = reinterpret_cast(g->vertexData()); + for (int i = 0; i < g->vertexCount(); ++i) + vdst[i].set(vdst[i].x, vdst[i].y, d->strokeColor); + return; + } + + g->allocate(d->strokeVertices.count(), 0); + g->setDrawingMode(QSGGeometry::DrawTriangleStrip); + memcpy(g->vertexData(), d->strokeVertices.constData(), g->vertexCount() * g->sizeOfVertex()); +} + +QSGMaterial *QQuickPathItemGenericMaterialFactory::createVertexColor(QQuickWindow *window) +{ + QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); + +#ifndef QT_NO_OPENGL + if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... + return new QSGVertexColorMaterial; +#endif + + qWarning("Vertex-color material: Unsupported graphics API %d", api); + return nullptr; +} + +QSGMaterial *QQuickPathItemGenericMaterialFactory::createLinearGradient(QQuickWindow *window, + QQuickPathItemGenericStrokeFillNode *node) +{ + QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); + +#ifndef QT_NO_OPENGL + if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... + return new QQuickPathItemLinearGradientMaterial(node); +#endif + + qWarning("Linear gradient material: Unsupported graphics API %d", api); + return nullptr; +} + +#ifndef QT_NO_OPENGL + +QSGMaterialType QQuickPathItemLinearGradientShader::type; + +QQuickPathItemLinearGradientShader::QQuickPathItemLinearGradientShader() +{ + setShaderSourceFile(QOpenGLShader::Vertex, + QStringLiteral(":/qt-project.org/items/shaders/lineargradient.vert")); + setShaderSourceFile(QOpenGLShader::Fragment, + QStringLiteral(":/qt-project.org/items/shaders/lineargradient.frag")); +} + +void QQuickPathItemLinearGradientShader::initialize() +{ + m_opacityLoc = program()->uniformLocation("opacity"); + m_matrixLoc = program()->uniformLocation("matrix"); + m_gradStartLoc = program()->uniformLocation("gradStart"); + m_gradEndLoc = program()->uniformLocation("gradEnd"); +} + +void QQuickPathItemLinearGradientShader::updateState(const RenderState &state, QSGMaterial *mat, QSGMaterial *) +{ + QQuickPathItemLinearGradientMaterial *m = static_cast(mat); + + if (state.isOpacityDirty()) + program()->setUniformValue(m_opacityLoc, state.opacity()); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); + + QQuickPathItemGenericStrokeFillNode *node = m->node(); + program()->setUniformValue(m_gradStartLoc, QVector2D(node->m_fillGradient.start)); + program()->setUniformValue(m_gradEndLoc, QVector2D(node->m_fillGradient.end)); + + QSGTexture *tx = QQuickPathItemGradientCache::currentCache()->get(node->m_fillGradient); + tx->bind(); +} + +char const *const *QQuickPathItemLinearGradientShader::attributeNames() const +{ + static const char *const attr[] = { "vertexCoord", "vertexColor", nullptr }; + return attr; +} + +int QQuickPathItemLinearGradientMaterial::compare(const QSGMaterial *other) const +{ + Q_ASSERT(other && type() == other->type()); + const QQuickPathItemLinearGradientMaterial *m = static_cast(other); + + QQuickPathItemGenericStrokeFillNode *a = node(); + QQuickPathItemGenericStrokeFillNode *b = m->node(); + Q_ASSERT(a && b); + if (a == b) + return 0; + + const QQuickPathItemGradientCache::GradientDesc *ga = &a->m_fillGradient; + const QQuickPathItemGradientCache::GradientDesc *gb = &b->m_fillGradient; + + if (int d = ga->spread - gb->spread) + return d; + + if (int d = ga->start.x() - gb->start.x()) + return d; + if (int d = ga->start.y() - gb->start.y()) + return d; + if (int d = ga->end.x() - gb->end.x()) + return d; + if (int d = ga->end.y() - gb->end.y()) + return d; + + if (int d = ga->stops.count() - gb->stops.count()) + return d; + + for (int i = 0; i < ga->stops.count(); ++i) { + if (int d = ga->stops[i].first - gb->stops[i].first) + return d; + if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba()) + return d; + } + + return 0; +} + +#endif // QT_NO_OPENGL + +QT_END_NAMESPACE diff --git a/src/imports/pathitem/qquickpathitemgenericrenderer_p.h b/src/imports/pathitem/qquickpathitemgenericrenderer_p.h new file mode 100644 index 0000000000..70a9e88d2f --- /dev/null +++ b/src/imports/pathitem/qquickpathitemgenericrenderer_p.h @@ -0,0 +1,303 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPATHITEMGENERICRENDERER_P_H +#define QQUICKPATHITEMGENERICRENDERER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickpathitem_p_p.h" +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickPathItemGenericNode; +class QQuickPathItemGenericStrokeFillNode; +class QQuickPathItemFillRunnable; +class QQuickPathItemStrokeRunnable; + +class QQuickPathItemGenericRenderer : public QQuickAbstractPathRenderer +{ +public: + enum Dirty { + DirtyFillGeom = 0x01, + DirtyStrokeGeom = 0x02, + DirtyColor = 0x04, + DirtyFillGradient = 0x08, + DirtyList = 0x10 // only for accDirty + }; + + QQuickPathItemGenericRenderer(QQuickItem *item) + : m_item(item), + m_api(QSGRendererInterface::Unknown), + m_rootNode(nullptr), + m_accDirty(0), + m_asyncCallback(nullptr) + { } + ~QQuickPathItemGenericRenderer(); + + void beginSync(int totalCount) override; + void setPath(int index, const QQuickPath *path) override; + void setJSPath(int index, const QQuickPathItemPath &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 &dashPattern) override; + void setFillGradient(int index, QQuickPathGradient *gradient) override; + void endSync(bool async) override; + void setAsyncCallback(void (*)(void *), void *) override; + Flags flags() const override { return SupportsAsync; } + + void updateNode() override; + + void setRootNode(QQuickPathItemGenericNode *node); + + struct Color4ub { unsigned char r, g, b, a; }; + typedef QVector VertexContainerType; + typedef QVector IndexContainerType; + + static void triangulateFill(const QPainterPath &path, + const Color4ub &fillColor, + VertexContainerType *fillVertices, + IndexContainerType *fillIndices, + QSGGeometry::Type *indexType, + bool supportsElementIndexUint); + static void triangulateStroke(const QPainterPath &path, + const QPen &pen, + const Color4ub &strokeColor, + VertexContainerType *strokeVertices, + const QSize &clipSize); + +private: + void maybeUpdateAsyncItem(); + + struct VisualPathData { + float strokeWidth; + QPen pen; + Color4ub strokeColor; + Color4ub fillColor; + Qt::FillRule fillRule; + QPainterPath path; + bool fillGradientActive; + QQuickPathItemGradientCache::GradientDesc fillGradient; + VertexContainerType fillVertices; + IndexContainerType fillIndices; + QSGGeometry::Type indexType; + VertexContainerType strokeVertices; + int syncDirty; + int effectiveDirty = 0; + QQuickPathItemFillRunnable *pendingFill = nullptr; + QQuickPathItemStrokeRunnable *pendingStroke = nullptr; + }; + + void updateShadowDataInNode(VisualPathData *d, QQuickPathItemGenericStrokeFillNode *n); + void updateFillNode(VisualPathData *d, QQuickPathItemGenericNode *node); + void updateStrokeNode(VisualPathData *d, QQuickPathItemGenericNode *node); + + QQuickItem *m_item; + QSGRendererInterface::GraphicsApi m_api; + QQuickPathItemGenericNode *m_rootNode; + QVector m_vp; + int m_accDirty; + void (*m_asyncCallback)(void *); + void *m_asyncCallbackData; +}; + +class QQuickPathItemFillRunnable : public QObject, public QRunnable +{ + Q_OBJECT + +public: + void run() override; + + bool orphaned = false; + + // input + QPainterPath path; + QQuickPathItemGenericRenderer::Color4ub fillColor; + bool supportsElementIndexUint; + + // output + QQuickPathItemGenericRenderer::VertexContainerType fillVertices; + QQuickPathItemGenericRenderer::IndexContainerType fillIndices; + QSGGeometry::Type indexType; + +Q_SIGNALS: + void done(QQuickPathItemFillRunnable *self); +}; + +class QQuickPathItemStrokeRunnable : public QObject, public QRunnable +{ + Q_OBJECT + +public: + void run() override; + + bool orphaned = false; + + // input + QPainterPath path; + QPen pen; + QQuickPathItemGenericRenderer::Color4ub strokeColor; + QSize clipSize; + + // output + QQuickPathItemGenericRenderer::VertexContainerType strokeVertices; + +Q_SIGNALS: + void done(QQuickPathItemStrokeRunnable *self); +}; + +class QQuickPathItemGenericStrokeFillNode : public QSGGeometryNode +{ +public: + QQuickPathItemGenericStrokeFillNode(QQuickWindow *window); + ~QQuickPathItemGenericStrokeFillNode(); + + enum Material { + MatSolidColor, + MatLinearGradient + }; + + void activateMaterial(Material m); + + QQuickWindow *window() const { return m_window; } + + // shadow data for custom materials + QQuickPathItemGradientCache::GradientDesc m_fillGradient; + +private: + QSGGeometry *m_geometry; + QQuickWindow *m_window; + QSGMaterial *m_material; + QScopedPointer m_solidColorMaterial; + QScopedPointer m_linearGradientMaterial; + + friend class QQuickPathItemGenericRenderer; +}; + +class QQuickPathItemGenericNode : public QSGNode +{ +public: + 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, QQuickPathItemGenericStrokeFillNode *node); +}; + +#ifndef QT_NO_OPENGL + +class QQuickPathItemLinearGradientShader : public QSGMaterialShader +{ +public: + QQuickPathItemLinearGradientShader(); + + void initialize() override; + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; + char const *const *attributeNames() const override; + + static QSGMaterialType type; + +private: + int m_opacityLoc; + int m_matrixLoc; + int m_gradStartLoc; + int m_gradEndLoc; +}; + +class QQuickPathItemLinearGradientMaterial : public QSGMaterial +{ +public: + QQuickPathItemLinearGradientMaterial(QQuickPathItemGenericStrokeFillNode *node) + : m_node(node) + { + // Passing RequiresFullMatrix is essential in order to prevent the + // batch renderer from baking in simple, translate-only transforms into + // the vertex data. The shader will rely on the fact that + // vertexCoord.xy is the PathItem-space coordinate and so no modifications + // are welcome. + setFlag(Blending | RequiresFullMatrix); + } + + QSGMaterialType *type() const override + { + return &QQuickPathItemLinearGradientShader::type; + } + + int compare(const QSGMaterial *other) const override; + + QSGMaterialShader *createShader() const override + { + return new QQuickPathItemLinearGradientShader; + } + + QQuickPathItemGenericStrokeFillNode *node() const { return m_node; } + +private: + QQuickPathItemGenericStrokeFillNode *m_node; +}; + +#endif // QT_NO_OPENGL + +QT_END_NAMESPACE + +#endif // QQUICKPATHITEMGENERICRENDERER_P_H diff --git a/src/imports/pathitem/qquickpathitemnvprrenderer.cpp b/src/imports/pathitem/qquickpathitemnvprrenderer.cpp new file mode 100644 index 0000000000..f8504f9985 --- /dev/null +++ b/src/imports/pathitem/qquickpathitemnvprrenderer.cpp @@ -0,0 +1,923 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickpathitemnvprrenderer_p.h" +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +void QQuickPathItemNvprRenderer::beginSync(int totalCount) +{ + if (m_vp.count() != totalCount) { + m_vp.resize(totalCount); + m_accDirty |= DirtyList; + } +} + +void QQuickPathItemNvprRenderer::setPath(int index, const QQuickPath *path) +{ + VisualPathGuiData &d(m_vp[index]); + convertPath(path, &d); + d.dirty |= DirtyPath; + m_accDirty |= DirtyPath; +} + +void QQuickPathItemNvprRenderer::setJSPath(int index, const QQuickPathItemPath &path) +{ + VisualPathGuiData &d(m_vp[index]); + convertJSPath(path, &d); + d.dirty |= DirtyPath; + m_accDirty |= DirtyPath; +} + +void QQuickPathItemNvprRenderer::setStrokeColor(int index, const QColor &color) +{ + VisualPathGuiData &d(m_vp[index]); + d.strokeColor = color; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; +} + +void QQuickPathItemNvprRenderer::setStrokeWidth(int index, qreal w) +{ + VisualPathGuiData &d(m_vp[index]); + d.strokeWidth = w; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; +} + +void QQuickPathItemNvprRenderer::setFillColor(int index, const QColor &color) +{ + VisualPathGuiData &d(m_vp[index]); + d.fillColor = color; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; +} + +void QQuickPathItemNvprRenderer::setFillRule(int index, QQuickVisualPath::FillRule fillRule) +{ + VisualPathGuiData &d(m_vp[index]); + d.fillRule = fillRule; + d.dirty |= DirtyFillRule; + m_accDirty |= DirtyFillRule; +} + +void QQuickPathItemNvprRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) +{ + VisualPathGuiData &d(m_vp[index]); + d.joinStyle = joinStyle; + d.miterLimit = miterLimit; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; +} + +void QQuickPathItemNvprRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) +{ + VisualPathGuiData &d(m_vp[index]); + d.capStyle = capStyle; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; +} + +void QQuickPathItemNvprRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) +{ + 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(int index, QQuickPathGradient *gradient) +{ + VisualPathGuiData &d(m_vp[index]); + d.fillGradientActive = gradient != nullptr; + if (gradient) { + d.fillGradient.stops = gradient->sortedGradientStops(); + d.fillGradient.spread = gradient->spread(); + if (QQuickPathLinearGradient *g = qobject_cast(gradient)) { + d.fillGradient.start = QPointF(g->x1(), g->y1()); + d.fillGradient.end = QPointF(g->x2(), g->y2()); + } else { + Q_UNREACHABLE(); + } + } + d.dirty |= DirtyFillGradient; + m_accDirty |= DirtyFillGradient; +} + +void QQuickPathItemNvprRenderer::endSync(bool) +{ +} + +void QQuickPathItemNvprRenderer::setNode(QQuickPathItemNvprRenderNode *node) +{ + if (m_node != node) { + m_node = node; + m_accDirty |= DirtyList; + } +} + +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) { + static struct { GLubyte cmd; const char *s; int coordCount; } nameTab[] = { + { GL_MOVE_TO_NV, "moveTo", 2 }, + { GL_LINE_TO_NV, "lineTo", 2 }, + { GL_QUADRATIC_CURVE_TO_NV, "quadTo", 4 }, + { GL_CUBIC_CURVE_TO_NV, "cubicTo", 6 }, + { GL_LARGE_CW_ARC_TO_NV, "arcTo-large-CW", 5 }, + { GL_LARGE_CCW_ARC_TO_NV, "arcTo-large-CCW", 5 }, + { GL_SMALL_CW_ARC_TO_NV, "arcTo-small-CW", 5 }, + { GL_SMALL_CCW_ARC_TO_NV, "arcTo-small-CCW", 5 }, + { GL_CLOSE_PATH_NV, "closePath", 0 } }; + for (size_t i = 0; i < sizeof(nameTab) / sizeof(nameTab[0]); ++i) { + if (nameTab[i].cmd == cmd) { + QByteArray cs; + for (int j = 0; j < nameTab[i].coordCount; ++j) { + cs.append(QByteArray::number(path.coord[ci++])); + cs.append(' '); + } + debug << "\n " << nameTab[i].s << " " << cs; + break; + } + } + } + return debug; +} + +static inline void appendCoords(QVector *v, QQuickCurve *c, QPointF *pos) +{ + QPointF p(c->hasRelativeX() ? pos->x() + c->relativeX() : c->x(), + c->hasRelativeY() ? pos->y() + c->relativeY() : c->y()); + v->append(p.x()); + v->append(p.y()); + *pos = p; +} + +static inline void appendControlCoords(QVector *v, QQuickPathQuad *c, const QPointF &pos) +{ + QPointF p(c->hasRelativeControlX() ? pos.x() + c->relativeControlX() : c->controlX(), + c->hasRelativeControlY() ? pos.y() + c->relativeControlY() : c->controlY()); + v->append(p.x()); + v->append(p.y()); +} + +static inline void appendControl1Coords(QVector *v, QQuickPathCubic *c, const QPointF &pos) +{ + QPointF p(c->hasRelativeControl1X() ? pos.x() + c->relativeControl1X() : c->control1X(), + c->hasRelativeControl1Y() ? pos.y() + c->relativeControl1Y() : c->control1Y()); + v->append(p.x()); + v->append(p.y()); +} + +static inline void appendControl2Coords(QVector *v, QQuickPathCubic *c, const QPointF &pos) +{ + QPointF p(c->hasRelativeControl2X() ? pos.x() + c->relativeControl2X() : c->control2X(), + c->hasRelativeControl2Y() ? pos.y() + c->relativeControl2Y() : c->control2Y()); + v->append(p.x()); + v->append(p.y()); +} + +void QQuickPathItemNvprRenderer::convertPath(const QQuickPath *path, VisualPathGuiData *d) +{ + d->path = NvprPath(); + if (!path) + return; + + const QList &pp(QQuickPathPrivate::get(path)->_pathElements); + if (pp.isEmpty()) + return; + + 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(e)) { + d->path.cmd.append(GL_MOVE_TO_NV); + appendCoords(&d->path.coord, o, &pos); + startPos = pos; + } else if (QQuickPathLine *o = qobject_cast(e)) { + d->path.cmd.append(GL_LINE_TO_NV); + appendCoords(&d->path.coord, o, &pos); + } else if (QQuickPathQuad *o = qobject_cast(e)) { + 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(e)) { + 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(e)) { + const bool sweepFlag = o->direction() == QQuickPathArc::Clockwise; // maps to CCW, not a typo + GLenum cmd; + if (o->useLargeArc()) + cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV; + else + cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV; + 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(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; + } + } + + // 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); +} + +void QQuickPathItemNvprRenderer::convertJSPath(const QQuickPathItemPath &path, VisualPathGuiData *d) +{ + d->path = NvprPath(); + if (path.cmd.isEmpty()) + return; + + QPointF startPos(0, 0); + QPointF pos(startPos); + int coordIdx = 0; + + for (QQuickPathItemPath::Command cmd : path.cmd) { + switch (cmd) { + case QQuickPathItemPath::MoveTo: + d->path.cmd.append(GL_MOVE_TO_NV); + pos = QPointF(path.coords[coordIdx], path.coords[coordIdx + 1]); + startPos = pos; + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 2; + break; + case QQuickPathItemPath::LineTo: + d->path.cmd.append(GL_LINE_TO_NV); + pos = QPointF(path.coords[coordIdx], path.coords[coordIdx + 1]); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 2; + break; + case QQuickPathItemPath::QuadTo: + d->path.cmd.append(GL_QUADRATIC_CURVE_TO_NV); + d->path.coord.append(path.coords[coordIdx]); + d->path.coord.append(path.coords[coordIdx + 1]); + pos = QPointF(path.coords[coordIdx + 2], path.coords[coordIdx + 3]); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 4; + break; + case QQuickPathItemPath::CubicTo: + d->path.cmd.append(GL_CUBIC_CURVE_TO_NV); + d->path.coord.append(path.coords[coordIdx]); + d->path.coord.append(path.coords[coordIdx + 1]); + d->path.coord.append(path.coords[coordIdx + 2]); + d->path.coord.append(path.coords[coordIdx + 3]); + pos = QPointF(path.coords[coordIdx + 4], path.coords[coordIdx + 5]); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 6; + break; + case QQuickPathItemPath::ArcTo: + { + const bool sweepFlag = !qFuzzyIsNull(path.coords[coordIdx + 5]); + const bool useLargeArc = !qFuzzyIsNull(path.coords[coordIdx + 6]); + GLenum cmd; + if (useLargeArc) + cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV; + else + cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV; + d->path.cmd.append(cmd); + d->path.coord.append(path.coords[coordIdx]); // rx + d->path.coord.append(path.coords[coordIdx + 1]); // ry + d->path.coord.append(path.coords[coordIdx + 2]); // xrot + pos = QPointF(path.coords[coordIdx + 3], path.coords[coordIdx + 4]); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 7; + } + break; + default: + qWarning("Unknown JS path command: %d", cmd); + break; + } + } + + if (pos == startPos) + d->path.cmd.append(GL_CLOSE_PATH_NV); +} + +static inline QVector4D qsg_premultiply(const QColor &c, float globalOpacity) +{ + const float o = c.alphaF() * globalOpacity; + return QVector4D(c.redF() * o, c.greenF() * o, c.blueF() * o, o); +} + +void QQuickPathItemNvprRenderer::updateNode() +{ + // Called on the render thread with gui blocked -> update the node with its + // own copy of all relevant data. + + if (!m_accDirty) + return; + + 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 (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 (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 (dirty & DirtyFillGradient) { + dst.fillGradientActive = src.fillGradientActive; + if (src.fillGradientActive) + dst.fillGradient = src.fillGradient; + } + } + + m_node->markDirty(QSGNode::DirtyMaterial); + m_accDirty = 0; +} + +bool QQuickPathItemNvprRenderNode::nvprInited = false; +QQuickNvprFunctions QQuickPathItemNvprRenderNode::nvpr; +QQuickNvprMaterialManager QQuickPathItemNvprRenderNode::mtlmgr; + +QQuickPathItemNvprRenderNode::~QQuickPathItemNvprRenderNode() +{ + releaseResources(); +} + +void QQuickPathItemNvprRenderNode::releaseResources() +{ + 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(); +} + +void QQuickNvprMaterialManager::create(QQuickNvprFunctions *nvpr) +{ + m_nvpr = nvpr; +} + +void QQuickNvprMaterialManager::releaseResources() +{ + QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); + for (MaterialDesc &mtl : m_materials) { + if (mtl.ppl) { + f->glDeleteProgramPipelines(1, &mtl.ppl); + mtl = MaterialDesc(); + } + } +} + +QQuickNvprMaterialManager::MaterialDesc *QQuickNvprMaterialManager::activateMaterial(Material m) +{ + QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); + MaterialDesc &mtl(m_materials[m]); + + if (!mtl.ppl) { + if (m == MatSolid) { + static const char *fragSrc = + "#version 310 es\n" + "precision highp float;\n" + "out vec4 fragColor;\n" + "uniform vec4 color;\n" + "uniform float opacity;\n" + "void main() {\n" + " fragColor = color * opacity;\n" + "}\n"; + if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { + qWarning("NVPR: Failed to create shader pipeline for solid fill"); + return nullptr; + } + Q_ASSERT(mtl.ppl && mtl.prg); + mtl.uniLoc[0] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "color"); + Q_ASSERT(mtl.uniLoc[0] >= 0); + mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); + Q_ASSERT(mtl.uniLoc[1] >= 0); + } else if (m == MatLinearGradient) { + static const char *fragSrc = + "#version 310 es\n" + "precision highp float;\n" + "layout(location = 0) in vec2 uv;" + "uniform float opacity;\n" + "uniform sampler2D gradTab;\n" + "uniform vec2 gradStart;\n" + "uniform vec2 gradEnd;\n" + "out vec4 fragColor;\n" + "void main() {\n" + " vec2 gradVec = gradEnd - gradStart;\n" + " float gradTabIndex = dot(gradVec, uv - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y);\n" + " fragColor = texture(gradTab, vec2(gradTabIndex, 0.5)) * opacity;\n" + "}\n"; + if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { + qWarning("NVPR: Failed to create shader pipeline for linear gradient"); + return nullptr; + } + Q_ASSERT(mtl.ppl && mtl.prg); + mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); + Q_ASSERT(mtl.uniLoc[1] >= 0); + mtl.uniLoc[2] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradStart"); + Q_ASSERT(mtl.uniLoc[2] >= 0); + mtl.uniLoc[3] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradEnd"); + Q_ASSERT(mtl.uniLoc[3] >= 0); + } else { + Q_UNREACHABLE(); + } + } + + f->glBindProgramPipeline(mtl.ppl); + + return &mtl; +} + +void QQuickPathItemNvprRenderNode::updatePath(VisualPathRenderData *d) +{ + 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()); + } + } + + 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 (d->dirty & QQuickPathItemNvprRenderer::DirtyDash) { + nvpr.pathParameterf(d->path, GL_PATH_DASH_OFFSET_NV, d->dashOffset); + // count == 0 -> no dash + nvpr.pathDashArray(d->path, d->dashPattern.count(), d->dashPattern.constData()); + } + + if (d->dirty) + d->fallbackValid = false; +} + +void QQuickPathItemNvprRenderNode::renderStroke(VisualPathRenderData *d, int strokeStencilValue, int writeMask) +{ + QQuickNvprMaterialManager::MaterialDesc *mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); + f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], + d->strokeColor.x(), d->strokeColor.y(), d->strokeColor.z(), d->strokeColor.w()); + f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); + + nvpr.stencilThenCoverStrokePath(d->path, strokeStencilValue, writeMask, GL_CONVEX_HULL_NV); +} + +void QQuickPathItemNvprRenderNode::renderFill(VisualPathRenderData *d) +{ + QQuickNvprMaterialManager::MaterialDesc *mtl = nullptr; + if (d->fillGradientActive) { + mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatLinearGradient); + 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 + // we need since the gradient's start and stop are in that space too. + GLfloat coeff[6] = { 1, 0, 0, + 0, 1, 0 }; + nvpr.programPathFragmentInputGen(mtl->prg, 0, GL_OBJECT_LINEAR_NV, 2, coeff); + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[2], 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], + 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(d->path, d->fillRule, writeMask, GL_BOUNDING_BOX_NV); +} + +void QQuickPathItemNvprRenderNode::renderOffscreenFill(VisualPathRenderData *d) +{ + if (d->fallbackValid && d->fallbackFbo) + return; + + GLfloat bb[4]; + nvpr.getPathParameterfv(d->path, GL_PATH_STROKE_BOUNDING_BOX_NV, bb); + QSize sz = QSizeF(bb[2] - bb[0] + 1, bb[3] - bb[1] + 1).toSize(); + d->fallbackSize = QSize(qMax(32, sz.width()), qMax(32, sz.height())); + d->fallbackTopLeft = QPointF(bb[0], bb[1]); + + if (d->fallbackFbo && d->fallbackFbo->size() != d->fallbackSize) { + delete d->fallbackFbo; + d->fallbackFbo = nullptr; + } + if (!d->fallbackFbo) + d->fallbackFbo = new QOpenGLFramebufferObject(d->fallbackSize, QOpenGLFramebufferObject::CombinedDepthStencil); + if (!d->fallbackFbo->bind()) + return; + + GLint prevViewport[4]; + f->glGetIntegerv(GL_VIEWPORT, prevViewport); + + f->glViewport(0, 0, d->fallbackSize.width(), d->fallbackSize.height()); + f->glDisable(GL_DEPTH_TEST); + f->glClearColor(0, 0, 0, 0); + f->glClearStencil(0); + f->glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); + f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + + QMatrix4x4 mv; + mv.translate(-d->fallbackTopLeft.x(), -d->fallbackTopLeft.y()); + nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, mv.constData()); + QMatrix4x4 proj; + proj.ortho(0, d->fallbackSize.width(), d->fallbackSize.height(), 0, 1, -1); + nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, proj.constData()); + + renderFill(d); + + d->fallbackFbo->release(); + f->glEnable(GL_DEPTH_TEST); + f->glViewport(prevViewport[0], prevViewport[1], prevViewport[2], prevViewport[3]); + + d->fallbackValid = true; +} + +void QQuickPathItemNvprRenderNode::setupStencilForCover(bool stencilClip, int sv) +{ + if (!stencilClip) { + // Assume stencil buffer is cleared to 0 for each frame. + // Within the frame dppass=GL_ZERO for glStencilOp ensures stencil is reset and so no need to clear. + f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); + f->glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO); + } else { + f->glStencilFunc(GL_LESS, sv, 0xFF); // pass if (sv & 0xFF) < (stencil_value & 0xFF) + f->glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); // dppass: replace with the original value (clip's stencil ref value) + } +} + +void QQuickPathItemNvprRenderNode::render(const RenderState *state) +{ + f = QOpenGLContext::currentContext()->extraFunctions(); + + if (!nvprInited) { + if (!nvpr.create()) { + qWarning("NVPR init failed"); + return; + } + mtlmgr.create(&nvpr); + nvprInited = true; + } + + f->glUseProgram(0); + f->glStencilMask(~0); + f->glEnable(GL_STENCIL_TEST); + + const bool stencilClip = state->stencilEnabled(); + // when true, the stencil buffer already has a clip path with a ref value of sv + const int sv = state->stencilValue(); + const bool hasScissor = state->scissorEnabled(); + + if (hasScissor) { + // scissor rect is already set, just enable scissoring + f->glEnable(GL_SCISSOR_TEST); + } + + // 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); + + bool reloadMatrices = true; + + for (VisualPathRenderData &d : m_vp) { + updatePath(&d); + + const bool hasFill = d.hasFill(); + const bool hasStroke = d.hasStroke(); + + 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... + if (hasScissor) + f->glDisable(GL_SCISSOR_TEST); + renderOffscreenFill(&d); + reloadMatrices = true; + if (hasScissor) + f->glEnable(GL_SCISSOR_TEST); + } + + if (reloadMatrices) { + reloadMatrices = false; + nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, matrix()->constData()); + nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, state->projectionMatrix()->constData()); + } + + // 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); + QMatrix4x4 mv = *matrix(); + mv.translate(d.fallbackTopLeft.x(), d.fallbackTopLeft.y()); + m_fallbackBlitter.texturedQuad(d.fallbackFbo->texture(), d.fallbackFbo->size(), + *state->projectionMatrix(), mv, + 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); + } + + renderStroke(&d, strokeStencilValue, writeMask); + } + + if (stencilClip) + nvpr.pathStencilFunc(GL_ALWAYS, 0, ~0); + + d.dirty = 0; + } + + f->glBindProgramPipeline(0); +} + +QSGRenderNode::StateFlags QQuickPathItemNvprRenderNode::changedStates() const +{ + return BlendState | StencilState | DepthState | ScissorState; +} + +QSGRenderNode::RenderingFlags QQuickPathItemNvprRenderNode::flags() const +{ + return DepthAwareRendering; // avoid hitting the less optimal no-opaque-batch path in the renderer +} + +bool QQuickPathItemNvprRenderNode::isSupported() +{ + static const bool nvprDisabled = qEnvironmentVariableIntValue("QT_NO_NVPR") != 0; + return !nvprDisabled && QQuickNvprFunctions::isSupported(); +} + +bool QQuickNvprBlitter::create() +{ + if (isCreated()) + destroy(); + + m_program = new QOpenGLShaderProgram; + if (QOpenGLContext::currentContext()->format().profile() == QSurfaceFormat::CoreProfile) { + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/shadereffect_core.vert")); + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/shadereffect_core.frag")); + } else { + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/shadereffect.vert")); + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/shadereffect.frag")); + } + m_program->bindAttributeLocation("qt_Vertex", 0); + m_program->bindAttributeLocation("qt_MultiTexCoord0", 1); + if (!m_program->link()) + return false; + + m_matrixLoc = m_program->uniformLocation("qt_Matrix"); + m_opacityLoc = m_program->uniformLocation("qt_Opacity"); + + m_buffer = new QOpenGLBuffer; + if (!m_buffer->create()) + return false; + m_buffer->bind(); + m_buffer->allocate(4 * sizeof(GLfloat) * 6); + m_buffer->release(); + + return true; +} + +void QQuickNvprBlitter::destroy() +{ + if (m_program) { + delete m_program; + m_program = nullptr; + } + if (m_buffer) { + delete m_buffer; + m_buffer = nullptr; + } +} + +void QQuickNvprBlitter::texturedQuad(GLuint textureId, const QSize &size, + const QMatrix4x4 &proj, const QMatrix4x4 &modelview, + float opacity) +{ + QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); + + m_program->bind(); + + QMatrix4x4 m = proj * modelview; + m_program->setUniformValue(m_matrixLoc, m); + m_program->setUniformValue(m_opacityLoc, opacity); + + m_buffer->bind(); + + if (size != m_prevSize) { + m_prevSize = size; + + QPointF p0(size.width() - 1, size.height() - 1); + QPointF p1(0, 0); + QPointF p2(0, size.height() - 1); + QPointF p3(size.width() - 1, 0); + + GLfloat vertices[6 * 4] = { + GLfloat(p0.x()), GLfloat(p0.y()), 1, 0, + GLfloat(p1.x()), GLfloat(p1.y()), 0, 1, + GLfloat(p2.x()), GLfloat(p2.y()), 0, 0, + + GLfloat(p0.x()), GLfloat(p0.y()), 1, 0, + GLfloat(p3.x()), GLfloat(p3.y()), 1, 1, + GLfloat(p1.x()), GLfloat(p1.y()), 0, 1, + }; + + m_buffer->write(0, vertices, sizeof(vertices)); + } + + m_program->enableAttributeArray(0); + m_program->enableAttributeArray(1); + f->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0); + f->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (const void *) (2 * sizeof(GLfloat))); + + f->glBindTexture(GL_TEXTURE_2D, textureId); + + f->glDrawArrays(GL_TRIANGLES, 0, 6); + + f->glBindTexture(GL_TEXTURE_2D, 0); + m_buffer->release(); + m_program->release(); +} + +QT_END_NAMESPACE diff --git a/src/imports/pathitem/qquickpathitemnvprrenderer_p.h b/src/imports/pathitem/qquickpathitemnvprrenderer_p.h new file mode 100644 index 0000000000..cfe1c7eab9 --- /dev/null +++ b/src/imports/pathitem/qquickpathitemnvprrenderer_p.h @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPATHITEMNVPRRENDERER_P_H +#define QQUICKPATHITEMNVPRRENDERER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickpathitem_p_p.h" +#include "qquicknvprfunctions_p.h" +#include +#include +#include +#include + +#ifndef QT_NO_OPENGL + +QT_BEGIN_NAMESPACE + +class QQuickPathItemNvprRenderNode; +class QOpenGLFramebufferObject; +class QOpenGLBuffer; +class QOpenGLExtraFunctions; + +class QQuickPathItemNvprRenderer : public QQuickAbstractPathRenderer +{ +public: + enum Dirty { + DirtyPath = 0x01, + DirtyStyle = 0x02, + DirtyFillRule = 0x04, + DirtyDash = 0x08, + DirtyFillGradient = 0x10, + DirtyList = 0x20 + }; + + void beginSync(int totalCount) override; + void setPath(int index, const QQuickPath *path) override; + void setJSPath(int index, const QQuickPathItemPath &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 &dashPattern) override; + void setFillGradient(int index, QQuickPathGradient *gradient) override; + void endSync(bool async) override; + + void updateNode() override; + + void setNode(QQuickPathItemNvprRenderNode *node); + + struct NvprPath { + QVector cmd; + QVector coord; + QByteArray str; + }; + +private: + 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 dashPattern; + bool fillGradientActive; + QQuickPathItemGradientCache::GradientDesc fillGradient; + }; + + void convertPath(const QQuickPath *path, VisualPathGuiData *d); + void convertJSPath(const QQuickPathItemPath &path, VisualPathGuiData *d); + + QQuickPathItemNvprRenderNode *m_node = nullptr; + int m_accDirty = 0; + + QVector m_vp; +}; + +QDebug operator<<(QDebug debug, const QQuickPathItemNvprRenderer::NvprPath &path); + +class QQuickNvprMaterialManager +{ +public: + enum Material { + MatSolid, + MatLinearGradient, + + NMaterials + }; + + struct MaterialDesc { + GLuint ppl = 0; + GLuint prg = 0; + int uniLoc[4]; + }; + + void create(QQuickNvprFunctions *nvpr); + MaterialDesc *activateMaterial(Material m); + void releaseResources(); + +private: + QQuickNvprFunctions *m_nvpr; + MaterialDesc m_materials[NMaterials]; +}; + +class QQuickNvprBlitter +{ +public: + bool create(); + void destroy(); + bool isCreated() const { return m_program != nullptr; } + void texturedQuad(GLuint textureId, const QSize &size, + const QMatrix4x4 &proj, const QMatrix4x4 &modelview, + float opacity); + +private: + QOpenGLShaderProgram *m_program = nullptr; + QOpenGLBuffer *m_buffer = nullptr; + int m_matrixLoc; + int m_opacityLoc; + QSize m_prevSize; +}; + +class QQuickPathItemNvprRenderNode : public QSGRenderNode +{ +public: + ~QQuickPathItemNvprRenderNode(); + + void render(const RenderState *state) override; + void releaseResources() override; + StateFlags changedStates() const override; + RenderingFlags flags() const override; + + static bool isSupported(); + +private: + 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 dashPattern; + bool fillGradientActive; + QQuickPathItemGradientCache::GradientDesc fillGradient; + QOpenGLFramebufferObject *fallbackFbo = nullptr; + bool fallbackValid = false; + QSize fallbackSize; + QPointF fallbackTopLeft; + + bool hasFill() const { return !qFuzzyIsNull(fillColor.w()) || fillGradientActive; } + bool hasStroke() const { return strokeWidth >= 0.0f && !qFuzzyIsNull(strokeColor.w()); } + }; + + 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; + static QQuickNvprFunctions nvpr; + static QQuickNvprMaterialManager mtlmgr; + + QQuickNvprBlitter m_fallbackBlitter; + QOpenGLExtraFunctions *f = nullptr; + + QVector m_vp; + + friend class QQuickPathItemNvprRenderer; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_OPENGL + +#endif // QQUICKPATHITEMNVPRRENDERER_P_H diff --git a/src/imports/pathitem/qquickpathitemsoftwarerenderer.cpp b/src/imports/pathitem/qquickpathitemsoftwarerenderer.cpp new file mode 100644 index 0000000000..b7aa93bf65 --- /dev/null +++ b/src/imports/pathitem/qquickpathitemsoftwarerenderer.cpp @@ -0,0 +1,277 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickpathitemsoftwarerenderer_p.h" +#include + +QT_BEGIN_NAMESPACE + +void QQuickPathItemSoftwareRenderer::beginSync(int totalCount) +{ + if (m_vp.count() != totalCount) { + m_vp.resize(totalCount); + m_accDirty |= DirtyList; + } +} + +void QQuickPathItemSoftwareRenderer::setPath(int index, const QQuickPath *path) +{ + VisualPathGuiData &d(m_vp[index]); + d.path = path ? path->path() : QPainterPath(); + d.dirty |= DirtyPath; + m_accDirty |= DirtyPath; +} + +void QQuickPathItemSoftwareRenderer::setJSPath(int index, const QQuickPathItemPath &path) +{ + VisualPathGuiData &d(m_vp[index]); + d.path = path.toPainterPath(); + d.dirty |= DirtyPath; + m_accDirty |= DirtyPath; +} + +void QQuickPathItemSoftwareRenderer::setStrokeColor(int index, const QColor &color) +{ + VisualPathGuiData &d(m_vp[index]); + d.pen.setColor(color); + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; +} + +void QQuickPathItemSoftwareRenderer::setStrokeWidth(int index, qreal w) +{ + VisualPathGuiData &d(m_vp[index]); + d.strokeWidth = w; + if (w >= 0.0f) + d.pen.setWidthF(w); + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; +} + +void QQuickPathItemSoftwareRenderer::setFillColor(int index, const QColor &color) +{ + VisualPathGuiData &d(m_vp[index]); + d.fillColor = color; + d.brush.setColor(color); + d.dirty |= DirtyBrush; + m_accDirty |= DirtyBrush; +} + +void QQuickPathItemSoftwareRenderer::setFillRule(int index, QQuickVisualPath::FillRule fillRule) +{ + VisualPathGuiData &d(m_vp[index]); + d.fillRule = Qt::FillRule(fillRule); + d.dirty |= DirtyFillRule; + m_accDirty |= DirtyFillRule; +} + +void QQuickPathItemSoftwareRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) +{ + VisualPathGuiData &d(m_vp[index]); + d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); + d.pen.setMiterLimit(miterLimit); + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; +} + +void QQuickPathItemSoftwareRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) +{ + VisualPathGuiData &d(m_vp[index]); + d.pen.setCapStyle(Qt::PenCapStyle(capStyle)); + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; +} + +void QQuickPathItemSoftwareRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) +{ + VisualPathGuiData &d(m_vp[index]); + switch (strokeStyle) { + case QQuickVisualPath::SolidLine: + d.pen.setStyle(Qt::SolidLine); + break; + case QQuickVisualPath::DashLine: + d.pen.setStyle(Qt::CustomDashLine); + d.pen.setDashPattern(dashPattern); + d.pen.setDashOffset(dashOffset); + break; + default: + break; + } + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; +} + +void QQuickPathItemSoftwareRenderer::setFillGradient(int index, QQuickPathGradient *gradient) +{ + VisualPathGuiData &d(m_vp[index]); + if (QQuickPathLinearGradient *linearGradient = qobject_cast(gradient)) { + QLinearGradient painterGradient(linearGradient->x1(), linearGradient->y1(), + linearGradient->x2(), linearGradient->y2()); + painterGradient.setStops(linearGradient->sortedGradientStops()); + switch (gradient->spread()) { + case QQuickPathGradient::PadSpread: + painterGradient.setSpread(QGradient::PadSpread); + break; + case QQuickPathGradient::RepeatSpread: + painterGradient.setSpread(QGradient::RepeatSpread); + break; + case QQuickPathGradient::ReflectSpread: + painterGradient.setSpread(QGradient::ReflectSpread); + break; + default: + break; + } + d.brush = QBrush(painterGradient); + } else { + d.brush = QBrush(d.fillColor); + } + d.dirty |= DirtyBrush; + m_accDirty |= DirtyBrush; +} + +void QQuickPathItemSoftwareRenderer::endSync(bool) +{ +} + +void QQuickPathItemSoftwareRenderer::setNode(QQuickPathItemSoftwareRenderNode *node) +{ + if (m_node != node) { + m_node = node; + m_accDirty |= DirtyList; + } +} + +void QQuickPathItemSoftwareRenderer::updateNode() +{ + if (!m_accDirty) + return; + + const int count = m_vp.count(); + const bool listChanged = m_accDirty & DirtyList; + if (listChanged) + m_node->m_vp.resize(count); + + m_node->m_boundingRect = QRectF(); + + for (int i = 0; i < count; ++i) { + VisualPathGuiData &src(m_vp[i]); + QQuickPathItemSoftwareRenderNode::VisualPathRenderData &dst(m_node->m_vp[i]); + + if (listChanged || (src.dirty & DirtyPath)) { + dst.path = src.path; + dst.path.setFillRule(src.fillRule); + } + + 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; + + src.dirty = 0; + + QRectF br = dst.path.boundingRect(); + const float sw = qMax(1.0f, dst.strokeWidth); + br.adjust(-sw, -sw, sw, sw); + m_node->m_boundingRect |= br; + } + + m_node->markDirty(QSGNode::DirtyMaterial); + m_accDirty = 0; +} + +QQuickPathItemSoftwareRenderNode::QQuickPathItemSoftwareRenderNode(QQuickPathItem *item) + : m_item(item) +{ +} + +QQuickPathItemSoftwareRenderNode::~QQuickPathItemSoftwareRenderNode() +{ + releaseResources(); +} + +void QQuickPathItemSoftwareRenderNode::releaseResources() +{ +} + +void QQuickPathItemSoftwareRenderNode::render(const RenderState *state) +{ + if (m_vp.isEmpty()) + return; + + QSGRendererInterface *rif = m_item->window()->rendererInterface(); + QPainter *p = static_cast(rif->getResource(m_item->window(), QSGRendererInterface::PainterResource)); + Q_ASSERT(p); + + const QRegion *clipRegion = state->clipRegion(); + if (clipRegion && !clipRegion->isEmpty()) + p->setClipRegion(*clipRegion, Qt::ReplaceClip); // must be done before setTransform + + p->setTransform(matrix()->toTransform()); + p->setOpacity(inheritedOpacity()); + + 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 +{ + return 0; +} + +QSGRenderNode::RenderingFlags QQuickPathItemSoftwareRenderNode::flags() const +{ + return BoundedRectRendering; // avoid fullscreen updates by saying we won't draw outside rect() +} + +QRectF QQuickPathItemSoftwareRenderNode::rect() const +{ + return m_boundingRect; +} + +QT_END_NAMESPACE diff --git a/src/imports/pathitem/qquickpathitemsoftwarerenderer_p.h b/src/imports/pathitem/qquickpathitemsoftwarerenderer_p.h new file mode 100644 index 0000000000..e76590bdfe --- /dev/null +++ b/src/imports/pathitem/qquickpathitemsoftwarerenderer_p.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPATHITEMSOFTWARERENDERER_P_H +#define QQUICKPATHITEMSOFTWARERENDERER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickpathitem_p_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickPathItemSoftwareRenderNode; + +class QQuickPathItemSoftwareRenderer : public QQuickAbstractPathRenderer +{ +public: + enum Dirty { + DirtyPath = 0x01, + DirtyPen = 0x02, + DirtyFillRule = 0x04, + DirtyBrush = 0x08, + DirtyList = 0x10 + }; + + void beginSync(int totalCount) override; + void setPath(int index, const QQuickPath *path) override; + void setJSPath(int index, const QQuickPathItemPath &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 &dashPattern) override; + void setFillGradient(int index, QQuickPathGradient *gradient) override; + void endSync(bool async) override; + + void updateNode() override; + + void setNode(QQuickPathItemSoftwareRenderNode *node); + +private: + QQuickPathItemSoftwareRenderNode *m_node = nullptr; + int m_accDirty = 0; + struct VisualPathGuiData { + int dirty = 0; + QPainterPath path; + QPen pen; + float strokeWidth; + QColor fillColor; + QBrush brush; + Qt::FillRule fillRule; + }; + QVector m_vp; +}; + +class QQuickPathItemSoftwareRenderNode : public QSGRenderNode +{ +public: + QQuickPathItemSoftwareRenderNode(QQuickPathItem *item); + ~QQuickPathItemSoftwareRenderNode(); + + void render(const RenderState *state) override; + void releaseResources() override; + StateFlags changedStates() const override; + RenderingFlags flags() const override; + QRectF rect() const override; + +private: + QQuickPathItem *m_item; + + struct VisualPathRenderData { + QPainterPath path; + QPen pen; + float strokeWidth; + QBrush brush; + }; + QVector m_vp; + QRectF m_boundingRect; + + friend class QQuickPathItemSoftwareRenderer; +}; + +QT_END_NAMESPACE + +#endif // QQUICKPATHITEMSOFTWARERENDERER_P_H diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index 1a6f530bfa..715fc4b2c7 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -40,6 +40,7 @@ #include "qquickcontext2d_p.h" #include "qquickcontext2dcommandbuffer_p.h" #include "qquickcanvasitem_p.h" +#include #include #include #if QT_CONFIG(quick_shadereffect) @@ -136,7 +137,7 @@ Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok); THROW_GENERIC_ERROR("Not a Context2D object"); #define qClamp(val, min, max) qMin(qMax(val, min), max) #define CHECK_RGBA(c) (c == '-' || c == '.' || (c >=0 && c <= 9)) -QColor qt_color_from_string(const QV4::Value &name) +Q_QUICK_PRIVATE_EXPORT QColor qt_color_from_string(const QV4::Value &name) { QByteArray str = name.toQString().toUtf8(); diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri index 511c6f18d8..0f8061b5ef 100644 --- a/src/quick/items/items.pri +++ b/src/quick/items/items.pri @@ -148,20 +148,9 @@ qtConfig(quick-listview) { qtConfig(quick-pathview) { HEADERS += \ $$PWD/qquickpathview_p.h \ - $$PWD/qquickpathview_p_p.h \ - $$PWD/qquickpathitem_p.h \ - $$PWD/qquickpathitem_p_p.h \ - $$PWD/qquickpathitemgenericrenderer_p.h \ - $$PWD/qquickpathitemsoftwarerenderer_p.h + $$PWD/qquickpathview_p_p.h SOURCES += \ - $$PWD/qquickpathview.cpp \ - $$PWD/qquickpathitem.cpp \ - $$PWD/qquickpathitemgenericrenderer.cpp \ - $$PWD/qquickpathitemsoftwarerenderer.cpp - qtConfig(opengl) { - HEADERS += $$PWD/qquickpathitemnvprrenderer_p.h - SOURCES += $$PWD/qquickpathitemnvprrenderer.cpp - } + $$PWD/qquickpathview.cpp } qtConfig(quick-positioners) { diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index 9e692da442..e6321e9365 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -70,7 +70,6 @@ #if QT_CONFIG(quick_path) #include #include -#include "qquickpathitem_p.h" #endif #if QT_CONFIG(quick_positioners) #include "qquickpositioners_p.h" @@ -381,11 +380,6 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) #if QT_CONFIG(quick_path) qmlRegisterType(uri, 2, 9, "PathArc"); qmlRegisterType(uri, 2, 9, "PathMove"); - qmlRegisterType(uri, 2, 9, "PathItem"); - qmlRegisterType(uri, 2, 9, "VisualPath"); - qmlRegisterType(uri, 2, 9, "PathGradientStop"); - qmlRegisterUncreatableType(uri, 2, 9, "PathGradient", QQuickPathGradient::tr("PathGradient is an abstract base class")); - qmlRegisterType(uri, 2, 9, "PathLinearGradient"); #endif qmlRegisterType(uri, 2, 9, "Text"); diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp deleted file mode 100644 index fae16064e5..0000000000 --- a/src/quick/items/qquickpathitem.cpp +++ /dev/null @@ -1,2258 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquickpathitem_p.h" -#include "qquickpathitem_p_p.h" -#include "qquickpathitemgenericrenderer_p.h" -#include "qquickpathitemnvprrenderer_p.h" -#include "qquickpathitemsoftwarerenderer_p.h" -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -QQuickPathItemStrokeFillParams::QQuickPathItemStrokeFillParams() - : strokeColor(Qt::white), - strokeWidth(1), - fillColor(Qt::white), - fillRule(QQuickVisualPath::OddEvenFill), - joinStyle(QQuickVisualPath::BevelJoin), - miterLimit(2), - capStyle(QQuickVisualPath::SquareCap), - strokeStyle(QQuickVisualPath::SolidLine), - dashOffset(0), - fillGradient(nullptr) -{ - dashPattern << 4 << 2; // 4 * strokeWidth dash followed by 2 * strokeWidth space -} - -QPainterPath QQuickPathItemPath::toPainterPath() const -{ - QPainterPath p; - int coordIdx = 0; - for (int i = 0; i < cmd.count(); ++i) { - switch (cmd[i]) { - case QQuickPathItemPath::MoveTo: - p.moveTo(coords[coordIdx], coords[coordIdx + 1]); - coordIdx += 2; - break; - case QQuickPathItemPath::LineTo: - p.lineTo(coords[coordIdx], coords[coordIdx + 1]); - coordIdx += 2; - break; - case QQuickPathItemPath::QuadTo: - p.quadTo(coords[coordIdx], coords[coordIdx + 1], - coords[coordIdx + 2], coords[coordIdx + 3]); - coordIdx += 4; - break; - case QQuickPathItemPath::CubicTo: - p.cubicTo(coords[coordIdx], coords[coordIdx + 1], - coords[coordIdx + 2], coords[coordIdx + 3], - coords[coordIdx + 4], coords[coordIdx + 5]); - coordIdx += 6; - break; - case QQuickPathItemPath::ArcTo: - // does not map to the QPainterPath API; reuse the helper code from QQuickSvgParser - QQuickSvgParser::pathArc(p, - coords[coordIdx], coords[coordIdx + 1], // radius - coords[coordIdx + 2], // xAxisRotation - !qFuzzyIsNull(coords[coordIdx + 6]), // useLargeArc - !qFuzzyIsNull(coords[coordIdx + 5]), // sweep flag - coords[coordIdx + 3], coords[coordIdx + 4], // end - p.currentPosition().x(), p.currentPosition().y()); - coordIdx += 7; - break; - default: - qWarning("Unknown JS path command: %d", cmd[i]); - break; - } - } - return p; -} - -/*! - \qmltype VisualPath - \instantiates QQuickVisualPath - \inqmlmodule QtQuick - \ingroup qtquick-paths - \ingroup qtquick-views - \inherits Object - \brief Describes a Path and associated properties for stroking and filling - \since 5.10 - - A PathItem contains one or more VisualPath elements. At least one - VisualPath is necessary in order to have a PathItem output anything - visible. A VisualPath in turn contains a Path and properties describing the - stroking and filling parameters, such as the stroke width and color, the - fill color or gradient, join and cap styles, and so on. Finally, the Path - object contains a list of path elements like PathMove, PathLine, PathCubic, - PathQuad, PathArc. - - Any property changes in these data sets will be bubble up and change the - output of the PathItem. This means that it is simple and easy to change, or - even animate, the starting and ending position, control points, or any - stroke or fill parameters using the usual QML bindings and animation types - like NumberAnimation. - - In the following example the line join style changes automatically based on - the value of joinStyleIndex: - - \code - VisualPath { - strokeColor: "black" - strokeWidth: 16 - fillColor: "transparent" - capStyle: VisualPath.RoundCap - - property int joinStyleIndex: 0 - property variant styles: [ VisualPath.BevelJoin, VisualPath.MiterJoin, VisualPath.RoundJoin ] - - joinStyle: styles[joinStyleIndex] - - Path { - startX: 30 - startY: 30 - PathLine { x: 100; y: 100 } - PathLine { x: 30; y: 100 } - } - } - \endcode - - Once associated with a PathItem, here is the output with a joinStyleIndex - of 2 (VisualPath.RoundJoin): - - \image visualpath-code-example.png - */ - -QQuickVisualPathPrivate::QQuickVisualPathPrivate() - : path(nullptr), - dirty(DirtyAll) -{ -} - -QQuickVisualPath::QQuickVisualPath(QObject *parent) - : QObject(*(new QQuickVisualPathPrivate), parent) -{ -} - -QQuickVisualPath::~QQuickVisualPath() -{ -} - -/*! - \qmlproperty Path QtQuick::VisualPath::path - - This property holds the Path object. - - \default - */ - -QQuickPath *QQuickVisualPath::path() const -{ - Q_D(const QQuickVisualPath); - return d->path; -} - -void QQuickVisualPath::setPath(QQuickPath *path) -{ - Q_D(QQuickVisualPath); - if (d->path == path) - return; - - if (d->path) - qmlobject_disconnect(d->path, QQuickPath, SIGNAL(changed()), - this, QQuickVisualPath, SLOT(_q_pathChanged())); - d->path = path; - qmlobject_connect(d->path, QQuickPath, SIGNAL(changed()), - this, QQuickVisualPath, SLOT(_q_pathChanged())); - - d->dirty |= QQuickVisualPathPrivate::DirtyPath; - emit pathChanged(); - emit changed(); -} - -void QQuickVisualPathPrivate::_q_pathChanged() -{ - Q_Q(QQuickVisualPath); - dirty |= DirtyPath; - emit q->changed(); -} - -/*! - \qmlproperty color QtQuick::VisualPath::strokeColor - - This property holds the stroking color. - - When set to \c transparent, no stroking occurs. - - The default value is \c white. - */ - -QColor QQuickVisualPath::strokeColor() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.strokeColor; -} - -void QQuickVisualPath::setStrokeColor(const QColor &color) -{ - Q_D(QQuickVisualPath); - if (d->sfp.strokeColor != color) { - d->sfp.strokeColor = color; - d->dirty |= QQuickVisualPathPrivate::DirtyStrokeColor; - emit strokeColorChanged(); - emit changed(); - } -} - -/*! - \qmlproperty color QtQuick::VisualPath::strokeWidth - - This property holds the stroke width. - - When set to a negative value, no stroking occurs. - - The default value is 1. - */ - -qreal QQuickVisualPath::strokeWidth() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.strokeWidth; -} - -void QQuickVisualPath::setStrokeWidth(qreal w) -{ - Q_D(QQuickVisualPath); - if (d->sfp.strokeWidth != w) { - d->sfp.strokeWidth = w; - d->dirty |= QQuickVisualPathPrivate::DirtyStrokeWidth; - emit strokeWidthChanged(); - emit changed(); - } -} - -/*! - \qmlproperty color QtQuick::VisualPath::fillColor - - This property holds the fill color. - - When set to \c transparent, no filling occurs. - - The default value is \c white. - */ - -QColor QQuickVisualPath::fillColor() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.fillColor; -} - -void QQuickVisualPath::setFillColor(const QColor &color) -{ - Q_D(QQuickVisualPath); - if (d->sfp.fillColor != color) { - d->sfp.fillColor = color; - d->dirty |= QQuickVisualPathPrivate::DirtyFillColor; - emit fillColorChanged(); - emit changed(); - } -} - -/*! - \qmlproperty enumeration QtQuick::VisualPath::fillRule - - This property holds the fill rule. The default value is - VisualPath.OddEvenFill. For an example on fill rules, see - QPainterPath::setFillRule(). - - \list - \li VisualPath.OddEvenFill - \li VisualPath.WindingFill - \endlist - */ - -QQuickVisualPath::FillRule QQuickVisualPath::fillRule() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.fillRule; -} - -void QQuickVisualPath::setFillRule(FillRule fillRule) -{ - Q_D(QQuickVisualPath); - if (d->sfp.fillRule != fillRule) { - d->sfp.fillRule = fillRule; - d->dirty |= QQuickVisualPathPrivate::DirtyFillRule; - emit fillRuleChanged(); - emit changed(); - } -} - -/*! - \qmlproperty enumeration QtQuick::VisualPath::joinStyle - - This property defines how joins between two connected lines are drawn. The - default value is VisualPath.BevelJoin. - - \list - \li VisualPath.MiterJoin - The outer edges of the lines are extended to meet at an angle, and this area is filled. - \li VisualPath.BevelJoin - The triangular notch between the two lines is filled. - \li VisualPath.RoundJoin - A circular arc between the two lines is filled. - \endlist - */ - -QQuickVisualPath::JoinStyle QQuickVisualPath::joinStyle() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.joinStyle; -} - -void QQuickVisualPath::setJoinStyle(JoinStyle style) -{ - Q_D(QQuickVisualPath); - if (d->sfp.joinStyle != style) { - d->sfp.joinStyle = style; - d->dirty |= QQuickVisualPathPrivate::DirtyStyle; - emit joinStyleChanged(); - emit changed(); - } -} - -/*! - \qmlproperty int QtQuick::VisualPath::miterLimit - - When VisualPath.joinStyle is set to VisualPath.MiterJoin, this property - specifies how far the miter join can extend from the join point. - - The default value is 2. - */ - -int QQuickVisualPath::miterLimit() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.miterLimit; -} - -void QQuickVisualPath::setMiterLimit(int limit) -{ - Q_D(QQuickVisualPath); - if (d->sfp.miterLimit != limit) { - d->sfp.miterLimit = limit; - d->dirty |= QQuickVisualPathPrivate::DirtyStyle; - emit miterLimitChanged(); - emit changed(); - } -} - -/*! - \qmlproperty enumeration QtQuick::VisualPath::capStyle - - This property defines how the end points of lines are drawn. The - default value is VisualPath.SquareCap. - - \list - \li VisualPath.FlatCap - A square line end that does not cover the end point of the line. - \li VisualPath.SquareCap - A square line end that covers the end point and extends beyond it by half the line width. - \li VisualPath.RoundCap - A rounded line end. - \endlist - */ - -QQuickVisualPath::CapStyle QQuickVisualPath::capStyle() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.capStyle; -} - -void QQuickVisualPath::setCapStyle(CapStyle style) -{ - Q_D(QQuickVisualPath); - if (d->sfp.capStyle != style) { - d->sfp.capStyle = style; - d->dirty |= QQuickVisualPathPrivate::DirtyStyle; - emit capStyleChanged(); - emit changed(); - } -} - -/*! - \qmlproperty enumeration QtQuick::VisualPath::strokeStyle - - This property defines the style of stroking. The default value is - VisualPath.SolidLine. - - \list - \li VisualPath.SolidLine - A plain line. - \li VisualPath.DashLine - Dashes separated by a few pixels. - \endlist - */ - -QQuickVisualPath::StrokeStyle QQuickVisualPath::strokeStyle() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.strokeStyle; -} - -void QQuickVisualPath::setStrokeStyle(StrokeStyle style) -{ - Q_D(QQuickVisualPath); - if (d->sfp.strokeStyle != style) { - d->sfp.strokeStyle = style; - d->dirty |= QQuickVisualPathPrivate::DirtyDash; - emit strokeStyleChanged(); - emit changed(); - } -} - -/*! - \qmlproperty real QtQuick::VisualPath::dashOffset - - This property defines the starting point on the dash pattern, measured in - units used to specify the dash pattern. - - The default value is 0. - - \sa QPen::setDashOffset() - */ - -qreal QQuickVisualPath::dashOffset() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.dashOffset; -} - -void QQuickVisualPath::setDashOffset(qreal offset) -{ - Q_D(QQuickVisualPath); - if (d->sfp.dashOffset != offset) { - d->sfp.dashOffset = offset; - d->dirty |= QQuickVisualPathPrivate::DirtyDash; - emit dashOffsetChanged(); - emit changed(); - } -} - -/*! - \qmlproperty list QtQuick::VisualPath::dashPattern - - This property defines the dash pattern when VisualPath.strokeStyle is set - to VisualPath.DashLine. The pattern must be specified as an even number of - positive entries where the entries 1, 3, 5... are the dashes and 2, 4, 6... - are the spaces. The pattern is specified in units of the pen's width. - - The default value is (4, 2), meaning a dash of 4 * VisualPath.strokeWidth - pixels followed by a space of 2 * VisualPath.strokeWidth pixels. - - \sa QPen::setDashPattern() - */ - -QVector QQuickVisualPath::dashPattern() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.dashPattern; -} - -void QQuickVisualPath::setDashPattern(const QVector &array) -{ - Q_D(QQuickVisualPath); - if (d->sfp.dashPattern != array) { - d->sfp.dashPattern = array; - d->dirty |= QQuickVisualPathPrivate::DirtyDash; - emit dashPatternChanged(); - emit changed(); - } -} - -/*! - \qmlproperty PathGradient QtQuick::VisualPath::fillGradient - - This property defines the fill gradient. By default no gradient is enabled - and the value is \c null. In this case the fill uses a solid color based on - the value of VisuaLPath.fillColor. - - When set, VisualPath.fillColor is ignored and filling is done using one of - the PathGradient subtypes. - */ - -QQuickPathGradient *QQuickVisualPath::fillGradient() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.fillGradient; -} - -void QQuickVisualPath::setFillGradient(QQuickPathGradient *gradient) -{ - Q_D(QQuickVisualPath); - if (d->sfp.fillGradient != gradient) { - if (d->sfp.fillGradient) - qmlobject_disconnect(d->sfp.fillGradient, QQuickPathGradient, SIGNAL(updated()), - this, QQuickVisualPath, SLOT(_q_fillGradientChanged())); - d->sfp.fillGradient = gradient; - if (d->sfp.fillGradient) - qmlobject_connect(d->sfp.fillGradient, QQuickPathGradient, SIGNAL(updated()), - this, QQuickVisualPath, SLOT(_q_fillGradientChanged())); - d->dirty |= QQuickVisualPathPrivate::DirtyFillGradient; - emit changed(); - } -} - -void QQuickVisualPathPrivate::_q_fillGradientChanged() -{ - Q_Q(QQuickVisualPath); - dirty |= DirtyFillGradient; - emit q->changed(); -} - -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 a GPU vendor 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 - PathItem is suitable for creating shapes spreading over larger areas of the - screen, avoiding the performance penalty for texture uploads or framebuffer - blits. In addition, the declarative API allows manipulating, binding to, - and even animating the path element properties like starting and ending - position, the control points, etc. - - 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, PathSvg. - - See \l Path for a detailed overview of the supported path elements. - - \code - PathItem { - width: 200 - height: 150 - anchors.centerIn: parent - VisualPath { - strokeWidth: 4 - strokeColor: "red" - fillGradient: PathLinearGradient { - x1: 20; y1: 20 - x2: 180; y2: 130 - PathGradientStop { position: 0; color: "blue" } - PathGradientStop { position: 0.2; color: "green" } - PathGradientStop { position: 0.4; color: "red" } - PathGradientStop { position: 0.6; color: "yellow" } - PathGradientStop { position: 1; color: "cyan" } - } - strokeStyle: VisualPath.DashLine - dashPattern: [ 1, 4 ] - Path { - startX: 20; startY: 20 - PathLine { x: 180; y: 130 } - PathLine { x: 20; y: 130 } - PathLine { x: 20; y: 20 } - } - } - } - \endcode - - \image pathitem-code-example.png - - \note 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. - - The following list summarizes the available PathItem rendering approaches: - - \list - - \li When running with the default, OpenGL backend of Qt Quick, both the - generic, triangulation-based and the NVIDIA-specific - \c{GL_NV_path_rendering} methods are available. The choice is made at - runtime, depending on the graphics driver's capabilities. When this is not - desired, applications can force using the generic method by setting the - PathItem.enableVendorExtensions property to \c false. - - \li The \c software backend is fully supported. The path is rendered via - QPainter::strokePath() and QPainter::fillPath() in this case. - - \li The Direct 3D 12 backend is not currently supported. - - \li The OpenVG backend is not currently supported. - - \endlist - - \sa Path, PathMove, PathLine, PathQuad, PathCubic, PathArc, PathSvg -*/ - -QQuickPathItemPrivate::QQuickPathItemPrivate() - : componentComplete(true), - vpChanged(false), - rendererType(QQuickPathItem::UnknownRenderer), - async(false), - status(QQuickPathItem::Null), - renderer(nullptr), - enableVendorExts(true) -{ -} - -QQuickPathItemPrivate::~QQuickPathItemPrivate() -{ - delete renderer; -} - -void QQuickPathItemPrivate::_q_visualPathChanged() -{ - Q_Q(QQuickPathItem); - vpChanged = true; - q->polish(); -} - -void QQuickPathItemPrivate::setStatus(QQuickPathItem::Status newStatus) -{ - Q_Q(QQuickPathItem); - if (status != newStatus) { - status = newStatus; - emit q->statusChanged(); - } -} - -QQuickPathItem::QQuickPathItem(QQuickItem *parent) - : QQuickItem(*(new QQuickPathItemPrivate), parent) -{ - setFlag(ItemHasContents); -} - -QQuickPathItem::~QQuickPathItem() -{ -} - -/*! - \qmlproperty enumeration QtQuick::PathItem::rendererType - - This property determines which path rendering backend is active. - - \list - - \li PathItem.UnknownRenderer - The renderer is unknown. - - \li PathItem.GeometryRenderer - The generic, driver independent solution - for OpenGL. Uses the same CPU-based triangulation approach as QPainter's - OpenGL 2 paint engine. This is the default on non-NVIDIA hardware when the - default, OpenGL Qt Quick scenegraph backend is in use. - - \li PathItem.NvprRenderer - Path items are rendered by performing OpenGL - calls using the \c{GL_NV_path_rendering} extension. This is the default on - NVIDIA hardware when the default, OpenGL Qt Quick scenegraph backend is in - use. - - \li PathItem.SoftwareRenderer - Pure QPainter drawing using the raster - paint engine. This is the default, and only, option when the Qt Quick - scenegraph is running with the \c software backend. - - \endlist -*/ - -QQuickPathItem::RendererType QQuickPathItem::rendererType() const -{ - Q_D(const QQuickPathItem); - return d->rendererType; -} - -/*! - \qmlproperty bool QtQuick::PathItem::asynchronous - - When PathItem.rendererType is PathItem.GeometryRenderer, the input path is - triangulated on the CPU during the polishing phase of the PathItem. This is - potentially expensive. To offload this work to separate worker threads, set - this property to \c true. - - When enabled, making a PathItem visible will not wait for the content to - become available. Instead, the gui/main thread is not blocked and the - results of the path rendering are shown only when all the asynchronous work - has been finished. - - The default value is \c false. - */ - -bool QQuickPathItem::asynchronous() const -{ - Q_D(const QQuickPathItem); - return d->async; -} - -void QQuickPathItem::setAsynchronous(bool async) -{ - Q_D(QQuickPathItem); - if (d->async != async) { - d->async = async; - emit asynchronousChanged(); - if (d->componentComplete) - d->_q_visualPathChanged(); - } -} - -/*! - \qmlproperty bool QtQuick::PathItem::enableVendorExtensions - - This property controls the usage of non-standard OpenGL extensions like - GL_NV_path_rendering. To disable PathItem.NvprRenderer and force a uniform - behavior regardless of the graphics card and drivers, set this property to - \c false. - - The default value is \c true. - */ - -bool QQuickPathItem::enableVendorExtensions() const -{ - Q_D(const QQuickPathItem); - return d->enableVendorExts; -} - -void QQuickPathItem::setEnableVendorExtensions(bool enable) -{ - Q_D(QQuickPathItem); - if (d->enableVendorExts != enable) { - d->enableVendorExts = enable; - emit enableVendorExtensionsChanged(); - } -} - -/*! - \qmlproperty enumeration QtQuick::PathItem::status - - This property determines the status of the PathItem and is relevant when - PathItem.asynchronous is set to \c true. - - \list - - \li PathItem.Null - Not yet initialized. - - \li PathItem.Ready - The PathItem has finished processing. - - \li PathItem.Processing - The path is being processed. - - \endlist - */ - -QQuickPathItem::Status QQuickPathItem::status() const -{ - Q_D(const QQuickPathItem); - return d->status; -} - -static QQuickVisualPath *vpe_at(QQmlListProperty *property, int index) -{ - QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast(property->object)); - return d->qmlData.vp.at(index); -} - -static void vpe_append(QQmlListProperty *property, QQuickVisualPath *obj) -{ - QQuickPathItem *item = static_cast(property->object); - QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(item); - d->qmlData.vp.append(obj); - - if (d->componentComplete) { - QObject::connect(obj, SIGNAL(changed()), item, SLOT(_q_visualPathChanged())); - d->_q_visualPathChanged(); - } -} - -static int vpe_count(QQmlListProperty *property) -{ - QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast(property->object)); - return d->qmlData.vp.count(); -} - -static void vpe_clear(QQmlListProperty *property) -{ - QQuickPathItem *item = static_cast(property->object); - QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(item); - - for (QQuickVisualPath *p : d->qmlData.vp) - QObject::disconnect(p, SIGNAL(changed()), item, SLOT(_q_visualPathChanged())); - - d->qmlData.vp.clear(); - - if (d->componentComplete) - d->_q_visualPathChanged(); -} - -/*! - \qmlproperty list QtQuick::PathItem::elements - - This property holds the VisualPath objects that define the contents of the - PathItem. - - \default - */ - -QQmlListProperty QQuickPathItem::elements() -{ - return QQmlListProperty(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->qmlData.vp) - connect(p, SIGNAL(changed()), this, SLOT(_q_visualPathChanged())); - - d->_q_visualPathChanged(); -} - -void QQuickPathItem::updatePolish() -{ - Q_D(QQuickPathItem); - - if (!d->vpChanged) - return; - - d->vpChanged = false; - - if (!d->renderer) { - d->createRenderer(); - if (!d->renderer) - return; - emit rendererChanged(); - } - - // endSync() is where expensive calculations may happen (or get kicked off - // on worker threads), depending on the backend. Therefore do this only - // when the item is visible. - if (isVisible()) - d->sync(); - - update(); -} - -void QQuickPathItem::itemChange(ItemChange change, const ItemChangeData &data) -{ - Q_D(QQuickPathItem); - - // sync may have been deferred; do it now if the item became visible - if (change == ItemVisibleHasChanged && data.boolValue) - d->_q_visualPathChanged(); - - QQuickItem::itemChange(change, data); -} - -QSGNode *QQuickPathItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *) -{ - // Called on the render thread, with the gui thread blocked. We can now - // safely access gui thread data. - - Q_D(QQuickPathItem); - if (d->renderer) { - if (!node) - node = d->createNode(); - d->renderer->updateNode(); - } - return node; -} - -// the renderer object lives on the gui thread -void QQuickPathItemPrivate::createRenderer() -{ - Q_Q(QQuickPathItem); - QSGRendererInterface *ri = q->window()->rendererInterface(); - if (!ri) - return; - - switch (ri->graphicsApi()) { -#ifndef QT_NO_OPENGL - case QSGRendererInterface::OpenGL: - if (enableVendorExts && QQuickPathItemNvprRenderNode::isSupported()) { - rendererType = QQuickPathItem::NvprRenderer; - renderer = new QQuickPathItemNvprRenderer; - } else { - rendererType = QQuickPathItem::GeometryRenderer; - renderer = new QQuickPathItemGenericRenderer(q); - } - break; -#endif - case QSGRendererInterface::Software: - rendererType = QQuickPathItem::SoftwareRenderer; - renderer = new QQuickPathItemSoftwareRenderer; - break; - default: - qWarning("No path backend for this graphics API yet"); - break; - } -} - -// the node lives on the render thread -QSGNode *QQuickPathItemPrivate::createNode() -{ - Q_Q(QQuickPathItem); - QSGNode *node = nullptr; - if (!q->window()) - return node; - QSGRendererInterface *ri = q->window()->rendererInterface(); - if (!ri) - return node; - - switch (ri->graphicsApi()) { -#ifndef QT_NO_OPENGL - case QSGRendererInterface::OpenGL: - if (enableVendorExts && QQuickPathItemNvprRenderNode::isSupported()) { - node = new QQuickPathItemNvprRenderNode; - static_cast(renderer)->setNode( - static_cast(node)); - } else { - node = new QQuickPathItemGenericNode; - static_cast(renderer)->setRootNode( - static_cast(node)); - } - break; -#endif - case QSGRendererInterface::Software: - node = new QQuickPathItemSoftwareRenderNode(q); - static_cast(renderer)->setNode( - static_cast(node)); - break; - default: - qWarning("No path backend for this graphics API yet"); - break; - } - - return node; -} - -static void q_asyncPathItemReady(void *data) -{ - QQuickPathItemPrivate *self = static_cast(data); - self->setStatus(QQuickPathItem::Ready); -} - -void QQuickPathItemPrivate::sync() -{ - const bool useAsync = async && renderer->flags().testFlag(QQuickAbstractPathRenderer::SupportsAsync); - if (useAsync) { - setStatus(QQuickPathItem::Processing); - renderer->setAsyncCallback(q_asyncPathItemReady, this); - } - - if (!jsData.isValid()) { - // Standard route: The path and stroke/fill parameters are provided via - // VisualPath and Path. - const int count = qmlData.vp.count(); - renderer->beginSync(count); - - for (int i = 0; i < count; ++i) { - QQuickVisualPath *p = qmlData.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; - } - - renderer->endSync(useAsync); - } else { - // Path and stroke/fill params provided from JavaScript. This avoids - // QObjects at the expense of not supporting changes afterwards. - const int count = jsData.paths.count(); - renderer->beginSync(count); - - for (int i = 0; i < count; ++i) { - renderer->setJSPath(i, jsData.paths[i]); - const QQuickPathItemStrokeFillParams sfp(jsData.sfp[i]); - renderer->setStrokeColor(i, sfp.strokeColor); - renderer->setStrokeWidth(i, sfp.strokeWidth); - renderer->setFillColor(i, sfp.fillColor); - renderer->setFillRule(i, sfp.fillRule); - renderer->setJoinStyle(i, sfp.joinStyle, sfp.miterLimit); - renderer->setCapStyle(i, sfp.capStyle); - renderer->setStrokeStyle(i, sfp.strokeStyle, sfp.dashOffset, sfp.dashPattern); - renderer->setFillGradient(i, sfp.fillGradient); - } - - renderer->endSync(useAsync); - } - - if (!useAsync) - setStatus(QQuickPathItem::Ready); -} - -// ***** gradient support ***** - -/*! - \qmltype PathGradientStop - \instantiates QQuickPathGradientStop - \inqmlmodule QtQuick - \ingroup qtquick-paths - \ingroup qtquick-views - \inherits Object - \brief Defines a color at a position in a gradient - \since 5.10 - */ - -QQuickPathGradientStop::QQuickPathGradientStop(QObject *parent) - : QObject(parent), - m_position(0), - m_color(Qt::black) -{ -} - -/*! - \qmlproperty real QtQuick::PathGradientStop::position - - The position and color properties describe the color used at a given - position in a gradient, as represented by a gradient stop. - - The default value is 0. - */ - -qreal QQuickPathGradientStop::position() const -{ - return m_position; -} - -void QQuickPathGradientStop::setPosition(qreal position) -{ - if (m_position != position) { - m_position = position; - if (QQuickPathGradient *grad = qobject_cast(parent())) - emit grad->updated(); - } -} - -/*! - \qmlproperty real QtQuick::PathGradientStop::color - - The position and color properties describe the color used at a given - position in a gradient, as represented by a gradient stop. - - The default value is \c black. - */ - -QColor QQuickPathGradientStop::color() const -{ - return m_color; -} - -void QQuickPathGradientStop::setColor(const QColor &color) -{ - if (m_color != color) { - m_color = color; - if (QQuickPathGradient *grad = qobject_cast(parent())) - emit grad->updated(); - } -} - -/*! - \qmltype PathGradient - \instantiates QQuickPathGradient - \inqmlmodule QtQuick - \ingroup qtquick-paths - \ingroup qtquick-views - \inherits Object - \brief Base type of PathItem fill gradients - \since 5.10 - - This is an abstract base class for gradients like PathLinearGradient and - cannot be created directly. - */ - -QQuickPathGradient::QQuickPathGradient(QObject *parent) - : QObject(parent), - m_spread(PadSpread) -{ -} - -int QQuickPathGradient::countStops(QQmlListProperty *list) -{ - QQuickPathGradient *grad = qobject_cast(list->object); - Q_ASSERT(grad); - return grad->m_stops.count(); -} - -QObject *QQuickPathGradient::atStop(QQmlListProperty *list, int index) -{ - QQuickPathGradient *grad = qobject_cast(list->object); - Q_ASSERT(grad); - return grad->m_stops.at(index); -} - -void QQuickPathGradient::appendStop(QQmlListProperty *list, QObject *stop) -{ - QQuickPathGradientStop *sstop = qobject_cast(stop); - if (!sstop) { - qWarning("Gradient stop list only supports QQuickPathGradientStop elements"); - return; - } - QQuickPathGradient *grad = qobject_cast(list->object); - Q_ASSERT(grad); - sstop->setParent(grad); - grad->m_stops.append(sstop); -} - -/*! - \qmlproperty list QtQuick::PathGradient::stops - \default - - The list of PathGradientStop objects defining the colors at given positions - in the gradient. - */ - -QQmlListProperty QQuickPathGradient::stops() -{ - return QQmlListProperty(this, nullptr, - &QQuickPathGradient::appendStop, - &QQuickPathGradient::countStops, - &QQuickPathGradient::atStop, - nullptr); -} - -QGradientStops QQuickPathGradient::sortedGradientStops() const -{ - QGradientStops result; - for (int i = 0; i < m_stops.count(); ++i) { - QQuickPathGradientStop *s = static_cast(m_stops[i]); - int j = 0; - while (j < result.count() && result[j].first < s->position()) - ++j; - result.insert(j, QGradientStop(s->position(), s->color())); - } - return result; -} - -/*! - \qmlproperty enumeration QtQuick::PathGradient::spred - - Specifies how the area outside the gradient area should be filled. The - default value is PathGradient.PadSpread. - - \list - \li PathGradient.PadSpread - The area is filled with the closest stop color. - \li PathGradient.RepeatSpread - The gradient is repeated outside the gradient area. - \li PathGradient.ReflectSpread - The gradient is reflected outside the gradient area. - \endlist - */ - -QQuickPathGradient::SpreadMode QQuickPathGradient::spread() const -{ - return m_spread; -} - -void QQuickPathGradient::setSpread(SpreadMode mode) -{ - if (m_spread != mode) { - m_spread = mode; - emit spreadChanged(); - emit updated(); - } -} - -/*! - \qmltype PathLinearGradient - \instantiates QQuickPathLinearGradient - \inqmlmodule QtQuick - \ingroup qtquick-paths - \ingroup qtquick-views - \inherits PathGradient - \brief Linear gradient - \since 5.10 - - Linear gradients interpolate colors between start and end points. Outside - these points the gradient is either padded, reflected or repeated depending - on the spread type. - - \sa QLinearGradient - */ - -QQuickPathLinearGradient::QQuickPathLinearGradient(QObject *parent) - : QQuickPathGradient(parent) -{ -} - -/*! - \qmlproperty real QtQuick::PathLinearGradient::x1 - \qmlproperty real QtQuick::PathLinearGradient::y1 - \qmlproperty real QtQuick::PathLinearGradient::x2 - \qmlproperty real QtQuick::PathLinearGradient::y2 - - These properties define the start and end points between which color - interpolation occurs. By default both the stard and end points are set to - (0, 0). - */ - -qreal QQuickPathLinearGradient::x1() const -{ - return m_start.x(); -} - -void QQuickPathLinearGradient::setX1(qreal v) -{ - if (m_start.x() != v) { - m_start.setX(v); - emit x1Changed(); - emit updated(); - } -} - -qreal QQuickPathLinearGradient::y1() const -{ - return m_start.y(); -} - -void QQuickPathLinearGradient::setY1(qreal v) -{ - if (m_start.y() != v) { - m_start.setY(v); - emit y1Changed(); - emit updated(); - } -} - -qreal QQuickPathLinearGradient::x2() const -{ - return m_end.x(); -} - -void QQuickPathLinearGradient::setX2(qreal v) -{ - if (m_end.x() != v) { - m_end.setX(v); - emit x2Changed(); - emit updated(); - } -} - -qreal QQuickPathLinearGradient::y2() const -{ - return m_end.y(); -} - -void QQuickPathLinearGradient::setY2(qreal v) -{ - if (m_end.y() != v) { - m_end.setY(v); - emit y2Changed(); - emit updated(); - } -} - -#ifndef QT_NO_OPENGL - -// contexts sharing with each other get the same cache instance -class QQuickPathItemGradientCacheWrapper -{ -public: - QQuickPathItemGradientCache *get(QOpenGLContext *context) - { - return m_resource.value(context); - } - -private: - QOpenGLMultiGroupSharedResource m_resource; -}; - -QQuickPathItemGradientCache *QQuickPathItemGradientCache::currentCache() -{ - static QQuickPathItemGradientCacheWrapper qt_path_gradient_caches; - return qt_path_gradient_caches.get(QOpenGLContext::currentContext()); -} - -// let QOpenGLContext manage the lifetime of the cached textures -QQuickPathItemGradientCache::~QQuickPathItemGradientCache() -{ - m_cache.clear(); -} - -void QQuickPathItemGradientCache::invalidateResource() -{ - m_cache.clear(); -} - -void QQuickPathItemGradientCache::freeResource(QOpenGLContext *) -{ - qDeleteAll(m_cache); - m_cache.clear(); -} - -static void generateGradientColorTable(const QQuickPathItemGradientCache::GradientDesc &gradient, - uint *colorTable, int size, float opacity) -{ - int pos = 0; - const QGradientStops &s = gradient.stops; - const bool colorInterpolation = true; - - uint alpha = qRound(opacity * 256); - uint current_color = ARGB_COMBINE_ALPHA(s[0].second.rgba(), alpha); - qreal incr = 1.0 / qreal(size); - qreal fpos = 1.5 * incr; - colorTable[pos++] = ARGB2RGBA(qPremultiply(current_color)); - - while (fpos <= s.first().first) { - colorTable[pos] = colorTable[pos - 1]; - pos++; - fpos += incr; - } - - if (colorInterpolation) - current_color = qPremultiply(current_color); - - const int sLast = s.size() - 1; - for (int i = 0; i < sLast; ++i) { - qreal delta = 1/(s[i+1].first - s[i].first); - uint next_color = ARGB_COMBINE_ALPHA(s[i + 1].second.rgba(), alpha); - if (colorInterpolation) - next_color = qPremultiply(next_color); - - while (fpos < s[i+1].first && pos < size) { - int dist = int(256 * ((fpos - s[i].first) * delta)); - int idist = 256 - dist; - if (colorInterpolation) - colorTable[pos] = ARGB2RGBA(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist)); - else - colorTable[pos] = ARGB2RGBA(qPremultiply(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist))); - ++pos; - fpos += incr; - } - current_color = next_color; - } - - Q_ASSERT(s.size() > 0); - - uint last_color = ARGB2RGBA(qPremultiply(ARGB_COMBINE_ALPHA(s[sLast].second.rgba(), alpha))); - for ( ; pos < size; ++pos) - colorTable[pos] = last_color; - - colorTable[size-1] = last_color; -} - -QSGTexture *QQuickPathItemGradientCache::get(const GradientDesc &grad) -{ - QSGPlainTexture *tx = m_cache[grad]; - if (!tx) { - QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); - GLuint id; - f->glGenTextures(1, &id); - f->glBindTexture(GL_TEXTURE_2D, id); - static const uint W = 1024; // texture size is 1024x1 - uint buf[W]; - generateGradientColorTable(grad, buf, W, 1.0f); - f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, W, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf); - tx = new QSGPlainTexture; - tx->setTextureId(id); - switch (grad.spread) { - case QQuickPathGradient::PadSpread: - tx->setHorizontalWrapMode(QSGTexture::ClampToEdge); - tx->setVerticalWrapMode(QSGTexture::ClampToEdge); - break; - case QQuickPathGradient::RepeatSpread: - tx->setHorizontalWrapMode(QSGTexture::Repeat); - tx->setVerticalWrapMode(QSGTexture::Repeat); - break; - case QQuickPathGradient::ReflectSpread: - tx->setHorizontalWrapMode(QSGTexture::MirroredRepeat); - tx->setVerticalWrapMode(QSGTexture::MirroredRepeat); - break; - default: - qWarning("Unknown gradient spread mode %d", grad.spread); - break; - } - m_cache[grad] = tx; - } - return tx; -} - -#endif // QT_NO_OPENGL - -// ***** JS-based alternative for creating static paths, (mostly) without QObjects ***** - -class QQuickPathItemJSEngineData : public QV8Engine::Deletable -{ -public: - QQuickPathItemJSEngineData(QV4::ExecutionEngine *engine); - - QV4::PersistentValue pathProto; - QV4::PersistentValue strokeFillParamsProto; -}; - -V4_DEFINE_EXTENSION(QQuickPathItemJSEngineData, engineData) - -namespace QV4 { -namespace Heap { - -struct QQuickPathItemJSPathPrototype : Object { - void init() { Object::init(); } -}; - -struct QQuickPathItemJSPath : Object { - void init() { Object::init(); } - QQuickPathItemPathObject *obj; -}; - -struct QQuickPathItemJSStrokeFillParamsPrototype : Object { - void init() { Object::init(); } -}; - -struct QQuickPathItemJSStrokeFillParams : Object { - void init() { Object::init(); } - QQuickPathItemStrokeFillParamsObject *obj; -}; - -} // namespace Heap -} // namespace QV4 - -struct QQuickPathItemJSPathPrototype : public QV4::Object -{ - V4_OBJECT2(QQuickPathItemJSPathPrototype, QV4::Object) -public: - static QV4::Heap::QQuickPathItemJSPathPrototype *create(QV4::ExecutionEngine *engine) - { - QV4::Scope scope(engine); - auto obj = engine->memoryManager->allocObject(); - QV4::Scoped o(scope, obj); - - o->defineDefaultProperty(QStringLiteral("clear"), method_clear, 0); - o->defineDefaultProperty(QStringLiteral("moveTo"), method_moveTo, 0); - o->defineDefaultProperty(QStringLiteral("lineTo"), method_lineTo, 0); - o->defineDefaultProperty(QStringLiteral("quadTo"), method_quadTo, 0); - o->defineDefaultProperty(QStringLiteral("cubicTo"), method_cubicTo, 0); - o->defineDefaultProperty(QStringLiteral("arcTo"), method_arcTo, 0); - - return o->d(); - } - - static void method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_moveTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_lineTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_quadTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_cubicTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_arcTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); -}; - -DEFINE_OBJECT_VTABLE(QQuickPathItemJSPathPrototype); - -struct QQuickPathItemJSStrokeFillParamsPrototype : public QV4::Object -{ - V4_OBJECT2(QQuickPathItemJSStrokeFillParamsPrototype, QV4::Object) -public: - static QV4::Heap::QQuickPathItemJSStrokeFillParamsPrototype *create(QV4::ExecutionEngine *engine) - { - QV4::Scope scope(engine); - auto obj = engine->memoryManager->allocObject(); - QV4::Scoped o(scope, obj); - - o->defineDefaultProperty(QStringLiteral("clear"), method_clear, 0); - - return o->d(); - } - - static void method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); -}; - -DEFINE_OBJECT_VTABLE(QQuickPathItemJSStrokeFillParamsPrototype); - -struct QQuickPathItemJSPath : public QV4::Object -{ - V4_OBJECT2(QQuickPathItemJSPath, QV4::Object) -}; - -DEFINE_OBJECT_VTABLE(QQuickPathItemJSPath); - -struct QQuickPathItemJSStrokeFillParams : public QV4::Object -{ - V4_OBJECT2(QQuickPathItemJSStrokeFillParams, QV4::Object) - - static void method_get_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); -}; - -DEFINE_OBJECT_VTABLE(QQuickPathItemJSStrokeFillParams); - -void QQuickPathItemJSPathPrototype::method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - r->d()->obj->clear(); - - scope.result = callData->thisObject.asReturnedValue(); -} - -void QQuickPathItemJSPathPrototype::method_moveTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - if (callData->argc >= 2) { - QQuickPathItemPathObject *p = r->d()->obj; - p->path.cmd.append(QQuickPathItemPath::MoveTo); - p->path.coords.append(callData->args[0].toNumber()); - p->path.coords.append(callData->args[1].toNumber()); - } - - scope.result = callData->thisObject.asReturnedValue(); -} - -void QQuickPathItemJSPathPrototype::method_lineTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - if (callData->argc >= 2) { - QQuickPathItemPathObject *p = r->d()->obj; - p->path.cmd.append(QQuickPathItemPath::LineTo); - p->path.coords.append(callData->args[0].toNumber()); - p->path.coords.append(callData->args[1].toNumber()); - } - - scope.result = callData->thisObject.asReturnedValue(); -} - -void QQuickPathItemJSPathPrototype::method_quadTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - if (callData->argc >= 4) { - QQuickPathItemPathObject *p = r->d()->obj; - p->path.cmd.append(QQuickPathItemPath::QuadTo); - const QV4::Value *v = callData->args; - p->path.coords.append(v[0].toNumber()); // cx - p->path.coords.append(v[1].toNumber()); // cy - p->path.coords.append(v[2].toNumber()); // x - p->path.coords.append(v[3].toNumber()); // y - } - - scope.result = callData->thisObject.asReturnedValue(); -} - -void QQuickPathItemJSPathPrototype::method_cubicTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - if (callData->argc >= 6) { - QQuickPathItemPathObject *p = r->d()->obj; - p->path.cmd.append(QQuickPathItemPath::CubicTo); - const QV4::Value *v = callData->args; - p->path.coords.append(v[0].toNumber()); // c1x - p->path.coords.append(v[1].toNumber()); // c1y - p->path.coords.append(v[2].toNumber()); // c2x - p->path.coords.append(v[3].toNumber()); // c2y - p->path.coords.append(v[4].toNumber()); // x - p->path.coords.append(v[5].toNumber()); // y - } - - scope.result = callData->thisObject.asReturnedValue(); -} - -void QQuickPathItemJSPathPrototype::method_arcTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - if (callData->argc >= 7) { - QQuickPathItemPathObject *p = r->d()->obj; - p->path.cmd.append(QQuickPathItemPath::ArcTo); - const QV4::Value *v = callData->args; - p->path.coords.append(v[0].toNumber()); // radiusX - p->path.coords.append(v[1].toNumber()); // radiusY - p->path.coords.append(v[2].toNumber()); // xAxisRotation - p->path.coords.append(v[3].toNumber()); // x - p->path.coords.append(v[4].toNumber()); // y - p->path.coords.append(v[5].toNumber()); // sweepFlag - p->path.coords.append(v[6].toNumber()); // largeArc - } - - scope.result = callData->thisObject.asReturnedValue(); -} - -void QQuickPathItemJSStrokeFillParamsPrototype::method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - r->d()->obj->clear(); - - scope.result = callData->thisObject.asReturnedValue(); -} - -extern QColor qt_color_from_string(const QV4::Value &name); // qquickcontext2d.cpp - -static inline QString qt_color_string(const QColor &color) -{ - if (color.alpha() == 255) - return color.name(); - QString alphaString = QString::number(color.alphaF(), 'f'); - while (alphaString.endsWith(QLatin1Char('0'))) - alphaString.chop(1); - if (alphaString.endsWith(QLatin1Char('.'))) - alphaString += QLatin1Char('0'); - return QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString); -} - -void QQuickPathItemJSStrokeFillParams::method_get_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = QV4::Encode(scope.engine->newString(qt_color_string(r->d()->obj->sfp.strokeColor))); -} - -void QQuickPathItemJSStrokeFillParams::method_set_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isString()) - r->d()->obj->sfp.strokeColor = qt_color_from_string(value); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.strokeWidth); -} - -void QQuickPathItemJSStrokeFillParams::method_set_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - r->d()->obj->sfp.strokeWidth = value->toNumber(); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = QV4::Encode(scope.engine->newString(qt_color_string(r->d()->obj->sfp.fillColor))); -} - -void QQuickPathItemJSStrokeFillParams::method_set_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isString()) - r->d()->obj->sfp.fillColor = qt_color_from_string(value); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.fillRule); -} - -void QQuickPathItemJSStrokeFillParams::method_set_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isInt32()) - r->d()->obj->sfp.fillRule = QQuickVisualPath::FillRule(value->integerValue()); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.joinStyle); -} - -void QQuickPathItemJSStrokeFillParams::method_set_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isInt32()) - r->d()->obj->sfp.joinStyle = QQuickVisualPath::JoinStyle(value->integerValue()); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.miterLimit); -} - -void QQuickPathItemJSStrokeFillParams::method_set_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - r->d()->obj->sfp.miterLimit = value->toNumber(); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.capStyle); -} - -void QQuickPathItemJSStrokeFillParams::method_set_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isInt32()) - r->d()->obj->sfp.capStyle = QQuickVisualPath::CapStyle(value->integerValue()); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.strokeStyle); -} - -void QQuickPathItemJSStrokeFillParams::method_set_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isInt32()) - r->d()->obj->sfp.strokeStyle = QQuickVisualPath::StrokeStyle(value->integerValue()); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.dashOffset); -} - -void QQuickPathItemJSStrokeFillParams::method_set_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - r->d()->obj->sfp.dashOffset = value->toNumber(); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedArrayObject a(scope, scope.engine->newArrayObject()); - QQuickPathItemStrokeFillParamsObject *p = r->d()->obj; - a->arrayReserve(p->sfp.dashPattern.count()); - QV4::ScopedValue v(scope); - for (int i = 0; i < p->sfp.dashPattern.count(); ++i) - a->arrayPut(i, (v = scope.engine->fromVariant(p->sfp.dashPattern[i]))); - a->setArrayLengthUnchecked(p->sfp.dashPattern.count()); - - scope.result = a.asReturnedValue(); -} - -void QQuickPathItemJSStrokeFillParams::method_set_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isObject()) { - QV4::Scoped ao(scope, value); - if (!!ao) { - QQuickPathItemStrokeFillParamsObject *p = r->d()->obj; - p->sfp.dashPattern.resize(ao->getLength()); - QV4::ScopedValue val(scope); - for (int i = 0; i < p->sfp.dashPattern.count(); ++i) { - val = ao->getIndexed(i); - p->sfp.dashPattern[i] = val->toNumber(); - } - } - } - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = r->d()->obj->v4fillGradient.value(); -} - -void QQuickPathItemJSStrokeFillParams::method_set_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - QV4::Scoped qobjectWrapper(scope, value); - if (!!qobjectWrapper) { - if (QQuickPathGradient *grad = qobject_cast(qobjectWrapper->object())) { - r->d()->obj->v4fillGradient.set(scope.engine, value); - r->d()->obj->sfp.fillGradient = grad; - } - } else { - r->d()->obj->v4fillGradient.set(scope.engine, nullptr); - r->d()->obj->sfp.fillGradient = nullptr; - } - - scope.result = QV4::Encode::undefined(); -} - -QQuickPathItemJSEngineData::QQuickPathItemJSEngineData(QV4::ExecutionEngine *v4) -{ - QV4::Scope scope(v4); - - QV4::ScopedObject proto(scope, QQuickPathItemJSPathPrototype::create(v4)); - pathProto = proto; - - proto = QV4::ScopedObject(scope, QQuickPathItemJSStrokeFillParamsPrototype::create(v4)); - - proto->defineAccessorProperty(QStringLiteral("strokeColor"), - QQuickPathItemJSStrokeFillParams::method_get_strokeColor, - QQuickPathItemJSStrokeFillParams::method_set_strokeColor); - proto->defineAccessorProperty(QStringLiteral("strokeWidth"), - QQuickPathItemJSStrokeFillParams::method_get_strokeWidth, - QQuickPathItemJSStrokeFillParams::method_set_strokeWidth); - proto->defineAccessorProperty(QStringLiteral("fillColor"), - QQuickPathItemJSStrokeFillParams::method_get_fillColor, - QQuickPathItemJSStrokeFillParams::method_set_fillColor); - proto->defineAccessorProperty(QStringLiteral("fillRule"), - QQuickPathItemJSStrokeFillParams::method_get_fillRule, - QQuickPathItemJSStrokeFillParams::method_set_fillRule); - proto->defineAccessorProperty(QStringLiteral("joinStyle"), - QQuickPathItemJSStrokeFillParams::method_get_joinStyle, - QQuickPathItemJSStrokeFillParams::method_set_joinStyle); - proto->defineAccessorProperty(QStringLiteral("miterLimit"), - QQuickPathItemJSStrokeFillParams::method_get_miterLimit, - QQuickPathItemJSStrokeFillParams::method_set_miterLimit); - proto->defineAccessorProperty(QStringLiteral("capStyle"), - QQuickPathItemJSStrokeFillParams::method_get_capStyle, - QQuickPathItemJSStrokeFillParams::method_set_capStyle); - proto->defineAccessorProperty(QStringLiteral("strokeStyle"), - QQuickPathItemJSStrokeFillParams::method_get_strokeStyle, - QQuickPathItemJSStrokeFillParams::method_set_strokeStyle); - proto->defineAccessorProperty(QStringLiteral("dashOffset"), - QQuickPathItemJSStrokeFillParams::method_get_dashOffset, - QQuickPathItemJSStrokeFillParams::method_set_dashOffset); - proto->defineAccessorProperty(QStringLiteral("dashPattern"), - QQuickPathItemJSStrokeFillParams::method_get_dashPattern, - QQuickPathItemJSStrokeFillParams::method_set_dashPattern); - proto->defineAccessorProperty(QStringLiteral("fillGradient"), - QQuickPathItemJSStrokeFillParams::method_get_fillGradient, - QQuickPathItemJSStrokeFillParams::method_set_fillGradient); - - strokeFillParamsProto = proto; -} - -void QQuickPathItemPathObject::setV4Engine(QV4::ExecutionEngine *engine) -{ - QQuickPathItemJSEngineData *ed = engineData(engine); - QV4::Scope scope(engine); - QV4::Scoped wrapper(scope, engine->memoryManager->allocObject()); - QV4::ScopedObject p(scope, ed->pathProto.value()); - wrapper->setPrototype(p); - wrapper->d()->obj = this; - m_v4value = wrapper; -} - -/*! - \qmltype JSPath - \inqmlmodule QtQuick - \ingroup qtquick-path - \brief Describes a path via a JavaScript API - */ - -/*! - \qmlmethod void QtQuick::JSPath::moveTo(x, y) - - Moves the path's position to the absolute position specified by (\a x, \a y). - */ - -/*! - \qmlmethod void QtQuick::JSPath::lineTo(x, y) - - Defines a straight line to the absolute position specified by (\a x, \a y). - */ - -/*! - \qmlmethod void QtQuick::JSPath::quadTo(cx, cy, x, y) - - Defines a quadratic Bezier curve with a control point (\a cx, \a cy) and an - end point of (\a x, \a y). - */ - -/*! - \qmlmethod void QtQuick::JSPath::cubicTo(c1x, c1y, c2x, c2y, x, y) - - Defines a cubic Bezier curve with two control points (\a c1x, \a c1y) and - (\a c2x, \a c2y), and an end point of (\a x, \a y). - */ - -/*! - \qmlmethod void QtQuick::JSPath::arcTo(radiusX, radiusY, xAxisRotation, x, y, sweepFlag, largeArc) - - Defines an elliptical arc, following the elliptical arc command in SVG. See - \l{https://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands}{the - SVG path specification} for details on the parameters. - */ - -/*! - \qmlmethod void QtQuick::JSPath::clear() - - Clears the path object removing all path elements. This is more lightweight - than creating a new JSPath object. - */ - -void QQuickPathItemPathObject::clear() -{ - path = QQuickPathItemPath(); -} - -/*! - \qmltype StrokeFillParams - \inqmlmodule QtQuick - \ingroup qtquick-path - \brief Describes stroke and fill parameters via a JavaScript API - - The properties of StrokeFillParams objects correspond 1:1 to VisualPath - properties. The possible values for enumerations are the same as well, for - example: - - \code - sfp.strokeStyle = VisualPath.DashLine; - sfp.capStyle = VisualPath.RoundCap; - \endcode - */ - -/*! - \qmlproperty color QtQuick::StrokeFillParams::strokeColor - */ - -/*! - \qmlproperty real QtQuick::StrokeFillParams::strokeWidth - */ - -/*! - \qmlproperty color QtQuick::StrokeFillParams::fillColor - */ - -/*! - \qmlproperty enumeration QtQuick::StrokeFillParams::fillRule - */ - -/*! - \qmlproperty enumeration QtQuick::StrokeFillParams::joinStyle - */ - -/*! - \qmlproperty int QtQuick::StrokeFillParams::miterLimit - */ - -/*! - \qmlproperty enumeration QtQuick::StrokeFillParams::capStyle - */ - -/*! - \qmlproperty enumeration QtQuick::StrokeFillParams::strokeStyle - */ - -/*! - \qmlproperty real QtQuick::StrokeFillParams::dashOffset - */ - -/*! - \qmlproperty list QtQuick::StrokeFillParams::dashPattern - - The dash pattern can be specified using JavaScript arrays. - - \code - sfp.dashPattern = [ 4, 2 ]; - \endcode - */ - -/*! - \qmlproperty object QtQuick::StrokeFillParams::fillGradient - - Sets the fill gradient. The default value is null. Gradients cannot be - created from JavaScript. Instead, reference a PathLinearGradient or other - item by id. - - \code - PathLinearGradient { id: grad; ... } - ... - sfp.fillGradient = grad; - \endcode - */ - -/*! - \qmlmethod void QtQuick::StrokeFillParams::clear() - - Resets all values to their defaults. This is more lightweight than creating - a new StrokeFillParams object. - */ - -void QQuickPathItemStrokeFillParamsObject::clear() -{ - sfp = QQuickPathItemStrokeFillParams(); - if (!v4fillGradient.isNullOrUndefined()) - v4fillGradient.set(v4fillGradient.engine(), nullptr); -} - -void QQuickPathItemStrokeFillParamsObject::setV4Engine(QV4::ExecutionEngine *engine) -{ - QQuickPathItemJSEngineData *ed = engineData(engine); - QV4::Scope scope(engine); - QV4::Scoped wrapper(scope, engine->memoryManager->allocObject()); - QV4::ScopedObject p(scope, ed->strokeFillParamsProto.value()); - wrapper->setPrototype(p); - wrapper->d()->obj = this; - m_v4value = wrapper; -} - -/*! - \qmlmethod JSPath QtQuick::PathItem::newPath() - - Creates and returns a new object that describes a path and offers a - JavaScript API. Paired with a stroke-fill parameter object it is - equivalent to a VisualPath. - - The following two snippets are equivalent when it comes to the end result: - - \code - var p = pathItem.newPath(); - var sfp = pathItem.newStrokeFillParams(); - sfp.fillColor = "white"; - sfp.strokeColor = "black"; - sfp.strokeWidth = 0.172; - p.moveTo(-122.304, 84.285); - p.cubicTo(-122.304, 84.285, -122.203, 86.179, -123.027, 86.16); - pathItem.appendVisualPath(p, sfp); - \endcode - - \code - PathItem { - VisualPath { - fillColor: "white" - strokeColor: "black" - strokeWidth: 0.172 - Path { - startX: -122.304; startY: 84.285 - PathCubic { control1X: -122.304; control1Y: 84.285; control2X: -122.203; control2Y: 86.179; x: -123.027; y: 86.16 } - } - } - } - \endcode - - The latter offers a full declarative API, with the possibility to binding - to and animating properties, while the former uses less resources due to - greatly reducing the number of QObject instances created. -*/ - -void QQuickPathItem::newPath(QQmlV4Function *args) -{ - QQuickPathItemPathObject *obj = new QQuickPathItemPathObject(this); - obj->setV4Engine(QQmlEnginePrivate::get(qmlEngine(this))->v4engine()); - args->setReturnValue(obj->v4value()); -} - -/*! - \qmlmethod StrokeFillParams QtQuick::PathItem::newStrokeFillParams() - - Creates and returns a new object that describes stroke and fill parameters - and offers a JavaScript API. Paired with a path object it is equivalent to - a VisualPath. - */ - -void QQuickPathItem::newStrokeFillParams(QQmlV4Function *args) -{ - QQuickPathItemStrokeFillParamsObject *obj = new QQuickPathItemStrokeFillParamsObject(this); - obj->setV4Engine(QQmlEnginePrivate::get(qmlEngine(this))->v4engine()); - args->setReturnValue(obj->v4value()); -} - -/*! - \qmlmethod void QtQuick::PathItem::clearVisualPaths() - - Clears the list of visual paths. - - \note This applies only to path and stroke-fill parameter objects registered - via appendVisualPaths(). - */ - -void QQuickPathItem::clearVisualPaths(QQmlV4Function *args) -{ - Q_UNUSED(args); - Q_D(QQuickPathItem); - d->jsData.paths.clear(); - d->jsData.sfp.clear(); -} - -/*! - \qmlmethod void QtQuick::PathItem::commitVisualPaths() - - Updates the PathItem. - - In order to avoid rendering a half-prepared PathItem, calling - PathItem.appendVisualPath() does not trigger any processing. Instead, - applications must call this function when all path and stroke-fill - parameter objects are registered. - */ - -void QQuickPathItem::commitVisualPaths(QQmlV4Function *args) -{ - Q_UNUSED(args); - Q_D(QQuickPathItem); - d->_q_visualPathChanged(); -} - -/*! - \qmlmethod void QtQuick::PathItem::appendVisualPath(object path, object strokeFillParams) - - Adds the visual path compoes of \a path and \a strokeFillParams into the - PathItem. - - \note The declarative and imprative (JavaScript) APIs of PathItem use - independent data structures. Calling this function has no effect on the - PathItem.elements property and vice versa. Once this function is called, - the PathItem will only consider the data registered via this function and - will ignore the declarative elements property. - */ - -void QQuickPathItem::appendVisualPath(QQmlV4Function *args) -{ - if (args->length() < 2) - return; - - Q_D(QQuickPathItem); - QV4::Scope scope(args->v4engine()); - QV4::Scoped jsp(scope, (*args)[0]); - QV4::Scoped jssfp(scope, (*args)[1]); - - const QQuickPathItemPath &path(jsp->d()->obj->path); - const QQuickPathItemStrokeFillParams &sfp(jssfp->d()->obj->sfp); - - d->jsData.paths.append(path); - d->jsData.sfp.append(sfp); -} - -QT_END_NAMESPACE - -#include "moc_qquickpathitem_p.cpp" diff --git a/src/quick/items/qquickpathitem_p.h b/src/quick/items/qquickpathitem_p.h deleted file mode 100644 index 37b23dee6f..0000000000 --- a/src/quick/items/qquickpathitem_p.h +++ /dev/null @@ -1,335 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKPATHITEM_P_H -#define QQUICKPATHITEM_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qquickitem.h" - -#include -#include -#include -#include - -QT_REQUIRE_CONFIG(quick_path); - -QT_BEGIN_NAMESPACE - -class QQuickVisualPathPrivate; -class QQuickPathItemPrivate; - -class Q_QUICK_PRIVATE_EXPORT QQuickPathGradientStop : public QObject -{ - Q_OBJECT - Q_PROPERTY(qreal position READ position WRITE setPosition) - Q_PROPERTY(QColor color READ color WRITE setColor) - -public: - QQuickPathGradientStop(QObject *parent = nullptr); - - qreal position() const; - void setPosition(qreal position); - - QColor color() const; - void setColor(const QColor &color); - -private: - qreal m_position; - QColor m_color; -}; - -class Q_QUICK_PRIVATE_EXPORT QQuickPathGradient : public QObject -{ - Q_OBJECT - Q_PROPERTY(QQmlListProperty stops READ stops) - Q_PROPERTY(SpreadMode spread READ spread WRITE setSpread NOTIFY spreadChanged) - Q_CLASSINFO("DefaultProperty", "stops") - -public: - enum SpreadMode { - PadSpread, - RepeatSpread, - ReflectSpread - }; - Q_ENUM(SpreadMode) - - QQuickPathGradient(QObject *parent = nullptr); - - QQmlListProperty stops(); - - QGradientStops sortedGradientStops() const; - - SpreadMode spread() const; - void setSpread(SpreadMode mode); - -signals: - void updated(); - void spreadChanged(); - -private: - static int countStops(QQmlListProperty *list); - static QObject *atStop(QQmlListProperty *list, int index); - static void appendStop(QQmlListProperty *list, QObject *stop); - - QVector m_stops; - SpreadMode m_spread; -}; - -class Q_QUICK_PRIVATE_EXPORT QQuickPathLinearGradient : public QQuickPathGradient -{ - Q_OBJECT - Q_PROPERTY(qreal x1 READ x1 WRITE setX1 NOTIFY x1Changed) - Q_PROPERTY(qreal y1 READ y1 WRITE setY1 NOTIFY y1Changed) - Q_PROPERTY(qreal x2 READ x2 WRITE setX2 NOTIFY x2Changed) - Q_PROPERTY(qreal y2 READ y2 WRITE setY2 NOTIFY y2Changed) - Q_CLASSINFO("DefaultProperty", "stops") - -public: - QQuickPathLinearGradient(QObject *parent = nullptr); - - qreal x1() const; - void setX1(qreal v); - qreal y1() const; - void setY1(qreal v); - qreal x2() const; - void setX2(qreal v); - qreal y2() const; - void setY2(qreal v); - -signals: - void x1Changed(); - void y1Changed(); - void x2Changed(); - void y2Changed(); - -private: - QPointF m_start; - QPointF m_end; -}; - -class Q_QUICK_PRIVATE_EXPORT QQuickVisualPath : public QObject -{ - Q_OBJECT - - 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) - Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged) - Q_PROPERTY(FillRule fillRule READ fillRule WRITE setFillRule NOTIFY fillRuleChanged) - Q_PROPERTY(JoinStyle joinStyle READ joinStyle WRITE setJoinStyle NOTIFY joinStyleChanged) - Q_PROPERTY(int miterLimit READ miterLimit WRITE setMiterLimit NOTIFY miterLimitChanged) - Q_PROPERTY(CapStyle capStyle READ capStyle WRITE setCapStyle NOTIFY capStyleChanged) - Q_PROPERTY(StrokeStyle strokeStyle READ strokeStyle WRITE setStrokeStyle NOTIFY strokeStyleChanged) - Q_PROPERTY(qreal dashOffset READ dashOffset WRITE setDashOffset NOTIFY dashOffsetChanged) - Q_PROPERTY(QVector dashPattern READ dashPattern WRITE setDashPattern NOTIFY dashPatternChanged) - Q_PROPERTY(QQuickPathGradient *fillGradient READ fillGradient WRITE setFillGradient RESET resetFillGradient) - -public: - enum FillRule { - OddEvenFill = Qt::OddEvenFill, - WindingFill = Qt::WindingFill - }; - Q_ENUM(FillRule) - - enum JoinStyle { - MiterJoin = Qt::MiterJoin, - BevelJoin = Qt::BevelJoin, - RoundJoin = Qt::RoundJoin - }; - Q_ENUM(JoinStyle) - - enum CapStyle { - FlatCap = Qt::FlatCap, - SquareCap = Qt::SquareCap, - RoundCap = Qt::RoundCap - }; - Q_ENUM(CapStyle) - - enum StrokeStyle { - SolidLine = Qt::SolidLine, - DashLine = Qt::DashLine - }; - Q_ENUM(StrokeStyle) - - QQuickVisualPath(QObject *parent = nullptr); - ~QQuickVisualPath(); - - QQuickPath *path() const; - void setPath(QQuickPath *path); - - QColor strokeColor() const; - void setStrokeColor(const QColor &color); - - qreal strokeWidth() const; - void setStrokeWidth(qreal w); - - QColor fillColor() const; - void setFillColor(const QColor &color); - - FillRule fillRule() const; - void setFillRule(FillRule fillRule); - - JoinStyle joinStyle() const; - void setJoinStyle(JoinStyle style); - - int miterLimit() const; - void setMiterLimit(int limit); - - CapStyle capStyle() const; - void setCapStyle(CapStyle style); - - StrokeStyle strokeStyle() const; - void setStrokeStyle(StrokeStyle style); - - qreal dashOffset() const; - void setDashOffset(qreal offset); - - QVector dashPattern() const; - void setDashPattern(const QVector &array); - - QQuickPathGradient *fillGradient() const; - void setFillGradient(QQuickPathGradient *gradient); - void resetFillGradient(); - -Q_SIGNALS: - void changed(); - void pathChanged(); - void strokeColorChanged(); - void strokeWidthChanged(); - void fillColorChanged(); - void fillRuleChanged(); - void joinStyleChanged(); - void miterLimitChanged(); - void capStyleChanged(); - void strokeStyleChanged(); - void dashOffsetChanged(); - void dashPatternChanged(); - void fillGradientChanged(); - -private: - Q_DISABLE_COPY(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(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) - Q_PROPERTY(bool enableVendorExtensions READ enableVendorExtensions WRITE setEnableVendorExtensions NOTIFY enableVendorExtensionsChanged) - Q_PROPERTY(Status status READ status NOTIFY statusChanged) - Q_PROPERTY(QQmlListProperty elements READ elements) - Q_CLASSINFO("DefaultProperty", "elements") - -public: - enum RendererType { - UnknownRenderer, - GeometryRenderer, - NvprRenderer, - SoftwareRenderer - }; - Q_ENUM(RendererType) - - enum Status { - Null, - Ready, - Processing - }; - Q_ENUM(Status) - - QQuickPathItem(QQuickItem *parent = nullptr); - ~QQuickPathItem(); - - RendererType rendererType() const; - - bool asynchronous() const; - void setAsynchronous(bool async); - - bool enableVendorExtensions() const; - void setEnableVendorExtensions(bool enable); - - Status status() const; - - QQmlListProperty elements(); - - Q_INVOKABLE void newPath(QQmlV4Function *args); - Q_INVOKABLE void newStrokeFillParams(QQmlV4Function *args); - Q_INVOKABLE void clearVisualPaths(QQmlV4Function *args); - Q_INVOKABLE void commitVisualPaths(QQmlV4Function *args); - Q_INVOKABLE void appendVisualPath(QQmlV4Function *args); - -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(); - void asynchronousChanged(); - void enableVendorExtensionsChanged(); - void statusChanged(); - -private: - Q_DISABLE_COPY(QQuickPathItem) - Q_DECLARE_PRIVATE(QQuickPathItem) - Q_PRIVATE_SLOT(d_func(), void _q_visualPathChanged()) -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQuickPathItem) - -#endif // QQUICKPATHITEM_P_H diff --git a/src/quick/items/qquickpathitem_p_p.h b/src/quick/items/qquickpathitem_p_p.h deleted file mode 100644 index c9a2904a25..0000000000 --- a/src/quick/items/qquickpathitem_p_p.h +++ /dev/null @@ -1,282 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKPATHITEM_P_P_H -#define QQUICKPATHITEM_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qquickpathitem_p.h" -#include "qquickitem_p.h" -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QSGPlainTexture; - -struct QQuickPathItemPath -{ - enum Command { - MoveTo, - LineTo, - QuadTo, - CubicTo, - ArcTo - }; - - QVector cmd; - QVector coords; - - QPainterPath toPainterPath() const; -}; - -struct QQuickPathItemStrokeFillParams -{ - QQuickPathItemStrokeFillParams(); - - QColor strokeColor; - qreal strokeWidth; - QColor fillColor; - QQuickVisualPath::FillRule fillRule; - QQuickVisualPath::JoinStyle joinStyle; - int miterLimit; - QQuickVisualPath::CapStyle capStyle; - QQuickVisualPath::StrokeStyle strokeStyle; - qreal dashOffset; - QVector dashPattern; - QQuickPathGradient *fillGradient; -}; - -class QQuickAbstractPathRenderer -{ -public: - enum Flag { - SupportsAsync = 0x01 - }; - Q_DECLARE_FLAGS(Flags, Flag) - - virtual ~QQuickAbstractPathRenderer() { } - - // Gui thread - virtual void beginSync(int totalCount) = 0; - virtual void endSync(bool async) = 0; - virtual void setAsyncCallback(void (*)(void *), void *) { } - virtual Flags flags() const { return 0; } - // - QML API - virtual void setPath(int index, const QQuickPath *path) = 0; - // - JS API - virtual void setJSPath(int index, const QQuickPathItemPath &path) = 0; - // - stroke/fill parameters - 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 &dashPattern) = 0; - virtual void setFillGradient(int index, QQuickPathGradient *gradient) = 0; - - // Render thread, with gui blocked - virtual void updateNode() = 0; -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickAbstractPathRenderer::Flags) - -class QQuickVisualPathPrivate : public QObjectPrivate -{ - Q_DECLARE_PUBLIC(QQuickVisualPath) - -public: - enum Dirty { - DirtyPath = 0x01, - DirtyStrokeColor = 0x02, - DirtyStrokeWidth = 0x04, - DirtyFillColor = 0x08, - DirtyFillRule = 0x10, - DirtyStyle = 0x20, - DirtyDash = 0x40, - DirtyFillGradient = 0x80, - - DirtyAll = 0xFF - }; - - QQuickVisualPathPrivate(); - - void _q_pathChanged(); - void _q_fillGradientChanged(); - - static QQuickVisualPathPrivate *get(QQuickVisualPath *p) { return p->d_func(); } - - QQuickPath *path; - int dirty; - QQuickPathItemStrokeFillParams sfp; -}; - -class QQuickPathItemPrivate : public QQuickItemPrivate -{ - Q_DECLARE_PUBLIC(QQuickPathItem) - -public: - QQuickPathItemPrivate(); - ~QQuickPathItemPrivate(); - - void createRenderer(); - QSGNode *createNode(); - void sync(); - - void _q_visualPathChanged(); - void setStatus(QQuickPathItem::Status newStatus); - - static QQuickPathItemPrivate *get(QQuickPathItem *item) { return item->d_func(); } - - bool componentComplete; - bool vpChanged; - QQuickPathItem::RendererType rendererType; - bool async; - QQuickPathItem::Status status; - QQuickAbstractPathRenderer *renderer; - - struct { - QVector vp; - } qmlData; - - struct { - bool isValid() const { Q_ASSERT(paths.count() == sfp.count()); return !paths.isEmpty(); } - QVector paths; - QVector sfp; - } jsData; - - bool enableVendorExts; -}; - -class QQuickPathItemPathObject : public QObject -{ - Q_OBJECT - -public: - QQuickPathItemPathObject(QObject *parent = nullptr) : QObject(parent) { } - - void setV4Engine(QV4::ExecutionEngine *engine); - QV4::ReturnedValue v4value() const { return m_v4value.value(); } - - QQuickPathItemPath path; - - void clear(); - -private: - QV4::PersistentValue m_v4value; -}; - -class QQuickPathItemStrokeFillParamsObject : public QObject -{ - Q_OBJECT - -public: - QQuickPathItemStrokeFillParamsObject(QObject *parent = nullptr) : QObject(parent) { } - - void setV4Engine(QV4::ExecutionEngine *engine); - QV4::ReturnedValue v4value() const { return m_v4value.value(); } - - QQuickPathItemStrokeFillParams sfp; - QV4::PersistentValue v4fillGradient; - - void clear(); - -private: - QV4::PersistentValue m_v4value; -}; - -#ifndef QT_NO_OPENGL - -class QQuickPathItemGradientCache : public QOpenGLSharedResource -{ -public: - struct GradientDesc { - QGradientStops stops; - QPointF start; - QPointF end; - QQuickPathGradient::SpreadMode spread; - bool operator==(const GradientDesc &other) const - { - return start == other.start && end == other.end && spread == other.spread - && stops == other.stops; - } - }; - - QQuickPathItemGradientCache(QOpenGLContext *context) : QOpenGLSharedResource(context->shareGroup()) { } - ~QQuickPathItemGradientCache(); - - void invalidateResource() override; - void freeResource(QOpenGLContext *) override; - - QSGTexture *get(const GradientDesc &grad); - - static QQuickPathItemGradientCache *currentCache(); - -private: - QHash m_cache; -}; - -inline uint qHash(const QQuickPathItemGradientCache::GradientDesc &v, uint seed = 0) -{ - uint h = seed; - h += v.start.x() + v.end.y() + v.spread; - for (int i = 0; i < 3 && i < v.stops.count(); ++i) - h += v.stops[i].second.rgba(); - return h; -} - -#endif // QT_NO_OPENGL - -QT_END_NAMESPACE - -#endif diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp deleted file mode 100644 index 4e8fe55df2..0000000000 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ /dev/null @@ -1,775 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquickpathitemgenericrenderer_p.h" -#include -#include -#include - -#ifndef QT_NO_OPENGL -#include -#include -#include -#include -#endif - -QT_BEGIN_NAMESPACE - -static const qreal TRI_SCALE = 1; - -struct ColoredVertex // must match QSGGeometry::ColoredPoint2D -{ - float x, y; - QQuickPathItemGenericRenderer::Color4ub color; - void set(float nx, float ny, QQuickPathItemGenericRenderer::Color4ub ncolor) - { - x = nx; y = ny; color = ncolor; - } -}; - -static inline QQuickPathItemGenericRenderer::Color4ub colorToColor4ub(const QColor &c) -{ - QQuickPathItemGenericRenderer::Color4ub color = { - uchar(qRound(c.redF() * c.alphaF() * 255)), - uchar(qRound(c.greenF() * c.alphaF() * 255)), - uchar(qRound(c.blueF() * c.alphaF() * 255)), - uchar(qRound(c.alphaF() * 255)) - }; - return color; -} - -QQuickPathItemGenericStrokeFillNode::QQuickPathItemGenericStrokeFillNode(QQuickWindow *window) - : m_geometry(new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0, 0)), - m_window(window), - m_material(nullptr) -{ - setGeometry(m_geometry); - activateMaterial(MatSolidColor); -#ifdef QSG_RUNTIME_DESCRIPTION - qsgnode_set_description(this, QLatin1String("stroke-fill")); -#endif -} - -QQuickPathItemGenericStrokeFillNode::~QQuickPathItemGenericStrokeFillNode() -{ - delete m_geometry; -} - -void QQuickPathItemGenericStrokeFillNode::activateMaterial(Material m) -{ - switch (m) { - case MatSolidColor: - // Use vertexcolor material. Items with different colors remain batchable - // this way, at the expense of having to provide per-vertex color values. - if (!m_solidColorMaterial) - m_solidColorMaterial.reset(QQuickPathItemGenericMaterialFactory::createVertexColor(m_window)); - m_material = m_solidColorMaterial.data(); - break; - case MatLinearGradient: - if (!m_linearGradientMaterial) - m_linearGradientMaterial.reset(QQuickPathItemGenericMaterialFactory::createLinearGradient(m_window, this)); - m_material = m_linearGradientMaterial.data(); - break; - default: - qWarning("Unknown material %d", m); - return; - } - - if (material() != m_material) - setMaterial(m_material); -} - -static bool q_supportsElementIndexUint(QSGRendererInterface::GraphicsApi api) -{ - static bool elementIndexUint = true; -#ifndef QT_NO_OPENGL - if (api == QSGRendererInterface::OpenGL) { - static bool elementIndexUintChecked = false; - if (!elementIndexUintChecked) { - elementIndexUintChecked = true; - QOpenGLContext *context = QOpenGLContext::currentContext(); - QScopedPointer dummyContext; - QScopedPointer dummySurface; - bool ok = true; - if (!context) { - dummyContext.reset(new QOpenGLContext); - dummyContext->create(); - context = dummyContext.data(); - dummySurface.reset(new QOffscreenSurface); - dummySurface->setFormat(context->format()); - dummySurface->create(); - ok = context->makeCurrent(dummySurface.data()); - } - if (ok) { - elementIndexUint = static_cast(context->functions())->hasOpenGLExtension( - QOpenGLExtensions::ElementIndexUint); - } - } - } -#else - Q_UNUSED(api); -#endif - return elementIndexUint; -} - -QQuickPathItemGenericRenderer::~QQuickPathItemGenericRenderer() -{ - for (VisualPathData &d : m_vp) { - if (d.pendingFill) - d.pendingFill->orphaned = true; - if (d.pendingStroke) - d.pendingStroke->orphaned = true; - } -} - -// sync, and so triangulation too, happens on the gui thread -// - except when async is set, in which case triangulation is moved to worker threads - -void QQuickPathItemGenericRenderer::beginSync(int totalCount) -{ - if (m_vp.count() != totalCount) { - m_vp.resize(totalCount); - m_accDirty |= DirtyList; - } - for (VisualPathData &d : m_vp) - d.syncDirty = 0; -} - -void QQuickPathItemGenericRenderer::setPath(int index, const QQuickPath *path) -{ - VisualPathData &d(m_vp[index]); - d.path = path ? path->path() : QPainterPath(); - d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom; -} - -void QQuickPathItemGenericRenderer::setJSPath(int index, const QQuickPathItemPath &path) -{ - VisualPathData &d(m_vp[index]); - d.path = path.toPainterPath(); - d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom; -} - -void QQuickPathItemGenericRenderer::setStrokeColor(int index, const QColor &color) -{ - VisualPathData &d(m_vp[index]); - d.strokeColor = colorToColor4ub(color); - d.syncDirty |= DirtyColor; -} - -void QQuickPathItemGenericRenderer::setStrokeWidth(int index, qreal w) -{ - VisualPathData &d(m_vp[index]); - d.strokeWidth = w; - if (w >= 0.0f) - d.pen.setWidthF(w); - d.syncDirty |= DirtyStrokeGeom; -} - -void QQuickPathItemGenericRenderer::setFillColor(int index, const QColor &color) -{ - VisualPathData &d(m_vp[index]); - d.fillColor = colorToColor4ub(color); - d.syncDirty |= DirtyColor; -} - -void QQuickPathItemGenericRenderer::setFillRule(int index, QQuickVisualPath::FillRule fillRule) -{ - VisualPathData &d(m_vp[index]); - d.fillRule = Qt::FillRule(fillRule); - d.syncDirty |= DirtyFillGeom; -} - -void QQuickPathItemGenericRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) -{ - VisualPathData &d(m_vp[index]); - d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); - d.pen.setMiterLimit(miterLimit); - d.syncDirty |= DirtyStrokeGeom; -} - -void QQuickPathItemGenericRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) -{ - VisualPathData &d(m_vp[index]); - d.pen.setCapStyle(Qt::PenCapStyle(capStyle)); - d.syncDirty |= DirtyStrokeGeom; -} - -void QQuickPathItemGenericRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, - qreal dashOffset, const QVector &dashPattern) -{ - VisualPathData &d(m_vp[index]); - d.pen.setStyle(Qt::PenStyle(strokeStyle)); - if (strokeStyle == QQuickVisualPath::DashLine) { - d.pen.setDashPattern(dashPattern); - d.pen.setDashOffset(dashOffset); - } - d.syncDirty |= DirtyStrokeGeom; -} - -void QQuickPathItemGenericRenderer::setFillGradient(int index, QQuickPathGradient *gradient) -{ - VisualPathData &d(m_vp[index]); - d.fillGradientActive = gradient != nullptr; - if (gradient) { - d.fillGradient.stops = gradient->sortedGradientStops(); - d.fillGradient.spread = gradient->spread(); - if (QQuickPathLinearGradient *g = qobject_cast(gradient)) { - d.fillGradient.start = QPointF(g->x1(), g->y1()); - d.fillGradient.end = QPointF(g->x2(), g->y2()); - } else { - Q_UNREACHABLE(); - } - } - d.syncDirty |= DirtyFillGradient; -} - -void QQuickPathItemFillRunnable::run() -{ - QQuickPathItemGenericRenderer::triangulateFill(path, fillColor, &fillVertices, &fillIndices, &indexType, supportsElementIndexUint); - emit done(this); -} - -void QQuickPathItemStrokeRunnable::run() -{ - QQuickPathItemGenericRenderer::triangulateStroke(path, pen, strokeColor, &strokeVertices, clipSize); - emit done(this); -} - -void QQuickPathItemGenericRenderer::setAsyncCallback(void (*callback)(void *), void *data) -{ - m_asyncCallback = callback; - m_asyncCallbackData = data; -} - -static QThreadPool *pathWorkThreadPool = nullptr; - -static void deletePathWorkThreadPool() -{ - delete pathWorkThreadPool; - pathWorkThreadPool = nullptr; -} - -void QQuickPathItemGenericRenderer::endSync(bool async) -{ - bool didKickOffAsync = false; - - for (int i = 0; i < m_vp.count(); ++i) { - VisualPathData &d(m_vp[i]); - 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; - } - - if (async && !pathWorkThreadPool) { - qAddPostRoutine(deletePathWorkThreadPool); - pathWorkThreadPool = new QThreadPool; - const int idealCount = QThread::idealThreadCount(); - pathWorkThreadPool->setMaxThreadCount(idealCount > 0 ? idealCount * 2 : 4); - } - - if ((d.syncDirty & DirtyFillGeom) && d.fillColor.a) { - d.path.setFillRule(d.fillRule); - if (m_api == QSGRendererInterface::Unknown) - m_api = m_item->window()->rendererInterface()->graphicsApi(); - if (async) { - QQuickPathItemFillRunnable *r = new QQuickPathItemFillRunnable; - r->setAutoDelete(false); - if (d.pendingFill) - d.pendingFill->orphaned = true; - d.pendingFill = r; - r->path = d.path; - r->fillColor = d.fillColor; - r->supportsElementIndexUint = q_supportsElementIndexUint(m_api); - // Unlikely in practice but in theory m_vp could be - // resized. Therefore, capture 'i' instead of 'd'. - QObject::connect(r, &QQuickPathItemFillRunnable::done, qApp, [this, i](QQuickPathItemFillRunnable *r) { - // Bail out when orphaned (meaning either another run was - // started after this one, or the renderer got destroyed). - if (!r->orphaned && i < m_vp.count()) { - VisualPathData &d(m_vp[i]); - d.fillVertices = r->fillVertices; - d.fillIndices = r->fillIndices; - d.indexType = r->indexType; - d.pendingFill = nullptr; - d.effectiveDirty |= DirtyFillGeom; - maybeUpdateAsyncItem(); - } - r->deleteLater(); - }); - didKickOffAsync = true; - pathWorkThreadPool->start(r); - } else { - triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices, &d.indexType, q_supportsElementIndexUint(m_api)); - } - } - - if ((d.syncDirty & DirtyStrokeGeom) && d.strokeWidth >= 0.0f && d.strokeColor.a) { - if (async) { - QQuickPathItemStrokeRunnable *r = new QQuickPathItemStrokeRunnable; - r->setAutoDelete(false); - if (d.pendingStroke) - d.pendingStroke->orphaned = true; - d.pendingStroke = r; - r->path = d.path; - r->pen = d.pen; - r->strokeColor = d.strokeColor; - r->clipSize = QSize(m_item->width(), m_item->height()); - QObject::connect(r, &QQuickPathItemStrokeRunnable::done, qApp, [this, i](QQuickPathItemStrokeRunnable *r) { - if (!r->orphaned && i < m_vp.count()) { - VisualPathData &d(m_vp[i]); - d.strokeVertices = r->strokeVertices; - d.pendingStroke = nullptr; - d.effectiveDirty |= DirtyStrokeGeom; - maybeUpdateAsyncItem(); - } - r->deleteLater(); - }); - didKickOffAsync = true; - pathWorkThreadPool->start(r); - } else { - triangulateStroke(d.path, d.pen, d.strokeColor, &d.strokeVertices, - QSize(m_item->width(), m_item->height())); - } - } - } - - if (!didKickOffAsync && async && m_asyncCallback) - m_asyncCallback(m_asyncCallbackData); -} - -void QQuickPathItemGenericRenderer::maybeUpdateAsyncItem() -{ - for (const VisualPathData &d : qAsConst(m_vp)) { - if (d.pendingFill || d.pendingStroke) - return; - } - m_accDirty |= DirtyFillGeom | DirtyStrokeGeom; - m_item->update(); - if (m_asyncCallback) - m_asyncCallback(m_asyncCallbackData); -} - -// the stroke/fill triangulation functions may be invoked either on the gui -// thread or some worker thread and must thus be self-contained. -void QQuickPathItemGenericRenderer::triangulateFill(const QPainterPath &path, - const Color4ub &fillColor, - VertexContainerType *fillVertices, - IndexContainerType *fillIndices, - QSGGeometry::Type *indexType, - bool supportsElementIndexUint) -{ - const QVectorPath &vp = qtVectorPathForPath(path); - - QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(TRI_SCALE, TRI_SCALE), 1, supportsElementIndexUint); - const int vertexCount = ts.vertices.count() / 2; // just a qreal vector with x,y hence the / 2 - fillVertices->resize(vertexCount); - ColoredVertex *vdst = reinterpret_cast(fillVertices->data()); - const qreal *vsrc = ts.vertices.constData(); - for (int i = 0; i < vertexCount; ++i) - vdst[i].set(vsrc[i * 2] / TRI_SCALE, vsrc[i * 2 + 1] / TRI_SCALE, fillColor); - - size_t indexByteSize; - if (ts.indices.type() == QVertexIndexVector::UnsignedShort) { - *indexType = QSGGeometry::UnsignedShortType; - // fillIndices is still QVector. Just resize to N/2 and pack - // the N quint16s into it. - fillIndices->resize(ts.indices.size() / 2); - indexByteSize = ts.indices.size() * sizeof(quint16); - } else { - *indexType = QSGGeometry::UnsignedIntType; - fillIndices->resize(ts.indices.size()); - indexByteSize = ts.indices.size() * sizeof(quint32); - } - memcpy(fillIndices->data(), ts.indices.data(), indexByteSize); -} - -void QQuickPathItemGenericRenderer::triangulateStroke(const QPainterPath &path, - const QPen &pen, - const Color4ub &strokeColor, - VertexContainerType *strokeVertices, - const QSize &clipSize) -{ - const QVectorPath &vp = qtVectorPathForPath(path); - const QRectF clip(QPointF(0, 0), clipSize); - const qreal inverseScale = 1.0 / TRI_SCALE; - - QTriangulatingStroker stroker; - stroker.setInvScale(inverseScale); - - if (pen.style() == Qt::SolidLine) { - stroker.process(vp, pen, clip, 0); - } else { - QDashedStrokeProcessor dashStroker; - dashStroker.setInvScale(inverseScale); - dashStroker.process(vp, pen, clip, 0); - QVectorPath dashStroke(dashStroker.points(), dashStroker.elementCount(), - dashStroker.elementTypes(), 0); - stroker.process(dashStroke, pen, clip, 0); - } - - if (!stroker.vertexCount()) { - strokeVertices->clear(); - return; - } - - const int vertexCount = stroker.vertexCount() / 2; // just a float vector with x,y hence the / 2 - strokeVertices->resize(vertexCount); - ColoredVertex *vdst = reinterpret_cast(strokeVertices->data()); - const float *vsrc = stroker.vertices(); - for (int i = 0; i < vertexCount; ++i) - vdst[i].set(vsrc[i * 2], vsrc[i * 2 + 1], strokeColor); -} - -void QQuickPathItemGenericRenderer::setRootNode(QQuickPathItemGenericNode *node) -{ - if (m_rootNode != node) { - m_rootNode = node; - m_accDirty |= DirtyList; - } -} - -// on the render thread with gui blocked -void QQuickPathItemGenericRenderer::updateNode() -{ - if (!m_rootNode || !m_accDirty) - return; - -// [ 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); - } - - QQuickPathItemGenericNode *node = *nodePtr; - - if (m_accDirty & DirtyList) - d.effectiveDirty |= DirtyFillGeom | DirtyStrokeGeom | DirtyColor | DirtyFillGradient; - - if (!d.effectiveDirty) { - prevNode = node; - nodePtr = &node->m_next; - 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 |= DirtyFillGeom; - } - - 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 |= DirtyStrokeGeom; - } - - updateFillNode(&d, node); - updateStrokeNode(&d, node); - - d.effectiveDirty = 0; - - prevNode = node; - nodePtr = &node->m_next; - } - - if (*nodePtr && prevNode) { - prevNode->removeChildNode(*nodePtr); - delete *nodePtr; - *nodePtr = nullptr; - } - - m_accDirty = 0; -} - -void QQuickPathItemGenericRenderer::updateShadowDataInNode(VisualPathData *d, QQuickPathItemGenericStrokeFillNode *n) -{ - if (d->fillGradientActive) { - if (d->effectiveDirty & DirtyFillGradient) - n->m_fillGradient = d->fillGradient; - } -} - -void QQuickPathItemGenericRenderer::updateFillNode(VisualPathData *d, QQuickPathItemGenericNode *node) -{ - if (!node->m_fillNode) - return; - if (!(d->effectiveDirty & (DirtyFillGeom | DirtyColor | DirtyFillGradient))) - return; - - // Make a copy of the data that will be accessed by the material on - // the render thread. This must be done even when we bail out below. - QQuickPathItemGenericStrokeFillNode *n = node->m_fillNode; - updateShadowDataInNode(d, n); - - QSGGeometry *g = n->m_geometry; - if (d->fillVertices.isEmpty()) { - if (g->vertexCount() || g->indexCount()) { - g->allocate(0, 0); - n->markDirty(QSGNode::DirtyGeometry); - } - return; - } - - if (d->fillGradientActive) { - n->activateMaterial(QQuickPathItemGenericStrokeFillNode::MatLinearGradient); - if (d->effectiveDirty & DirtyFillGradient) { - // 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 (!(d->effectiveDirty & DirtyFillGeom)) - return; - } - } else { - n->activateMaterial(QQuickPathItemGenericStrokeFillNode::MatSolidColor); - // fast path for updating only color values when no change in vertex positions - if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyFillGeom)) { - ColoredVertex *vdst = reinterpret_cast(g->vertexData()); - for (int i = 0; i < g->vertexCount(); ++i) - vdst[i].set(vdst[i].x, vdst[i].y, d->fillColor); - n->markDirty(QSGNode::DirtyGeometry); - return; - } - } - - const int indexCount = d->indexType == QSGGeometry::UnsignedShortType - ? d->fillIndices.count() * 2 : d->fillIndices.count(); - if (g->indexType() != d->indexType) { - g = new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), - d->fillVertices.count(), indexCount, d->indexType); - n->setGeometry(g); - delete n->m_geometry; - n->m_geometry = g; - } else { - g->allocate(d->fillVertices.count(), indexCount); - } - g->setDrawingMode(QSGGeometry::DrawTriangles); - 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(VisualPathData *d, QQuickPathItemGenericNode *node) -{ - if (!node->m_strokeNode) - return; - if (!(d->effectiveDirty & (DirtyStrokeGeom | DirtyColor))) - return; - - QQuickPathItemGenericStrokeFillNode *n = node->m_strokeNode; - QSGGeometry *g = n->m_geometry; - if (d->strokeVertices.isEmpty()) { - if (g->vertexCount() || g->indexCount()) { - g->allocate(0, 0); - n->markDirty(QSGNode::DirtyGeometry); - } - return; - } - - n->markDirty(QSGNode::DirtyGeometry); - - // Async loading runs update once, bails out above, then updates again once - // ready. Set the material dirty then. This is in-line with fill where the - // first activateMaterial() achieves the same. - if (!g->vertexCount()) - n->markDirty(QSGNode::DirtyMaterial); - - if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyStrokeGeom)) { - ColoredVertex *vdst = reinterpret_cast(g->vertexData()); - for (int i = 0; i < g->vertexCount(); ++i) - vdst[i].set(vdst[i].x, vdst[i].y, d->strokeColor); - return; - } - - g->allocate(d->strokeVertices.count(), 0); - g->setDrawingMode(QSGGeometry::DrawTriangleStrip); - memcpy(g->vertexData(), d->strokeVertices.constData(), g->vertexCount() * g->sizeOfVertex()); -} - -QSGMaterial *QQuickPathItemGenericMaterialFactory::createVertexColor(QQuickWindow *window) -{ - QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); - -#ifndef QT_NO_OPENGL - if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... - return new QSGVertexColorMaterial; -#endif - - qWarning("Vertex-color material: Unsupported graphics API %d", api); - return nullptr; -} - -QSGMaterial *QQuickPathItemGenericMaterialFactory::createLinearGradient(QQuickWindow *window, - QQuickPathItemGenericStrokeFillNode *node) -{ - QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); - -#ifndef QT_NO_OPENGL - if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... - return new QQuickPathItemLinearGradientMaterial(node); -#endif - - qWarning("Linear gradient material: Unsupported graphics API %d", api); - return nullptr; -} - -#ifndef QT_NO_OPENGL - -QSGMaterialType QQuickPathItemLinearGradientShader::type; - -QQuickPathItemLinearGradientShader::QQuickPathItemLinearGradientShader() -{ - setShaderSourceFile(QOpenGLShader::Vertex, - QStringLiteral(":/qt-project.org/items/shaders/lineargradient.vert")); - setShaderSourceFile(QOpenGLShader::Fragment, - QStringLiteral(":/qt-project.org/items/shaders/lineargradient.frag")); -} - -void QQuickPathItemLinearGradientShader::initialize() -{ - m_opacityLoc = program()->uniformLocation("opacity"); - m_matrixLoc = program()->uniformLocation("matrix"); - m_gradStartLoc = program()->uniformLocation("gradStart"); - m_gradEndLoc = program()->uniformLocation("gradEnd"); -} - -void QQuickPathItemLinearGradientShader::updateState(const RenderState &state, QSGMaterial *mat, QSGMaterial *) -{ - QQuickPathItemLinearGradientMaterial *m = static_cast(mat); - - if (state.isOpacityDirty()) - program()->setUniformValue(m_opacityLoc, state.opacity()); - - if (state.isMatrixDirty()) - program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); - - QQuickPathItemGenericStrokeFillNode *node = m->node(); - program()->setUniformValue(m_gradStartLoc, QVector2D(node->m_fillGradient.start)); - program()->setUniformValue(m_gradEndLoc, QVector2D(node->m_fillGradient.end)); - - QSGTexture *tx = QQuickPathItemGradientCache::currentCache()->get(node->m_fillGradient); - tx->bind(); -} - -char const *const *QQuickPathItemLinearGradientShader::attributeNames() const -{ - static const char *const attr[] = { "vertexCoord", "vertexColor", nullptr }; - return attr; -} - -int QQuickPathItemLinearGradientMaterial::compare(const QSGMaterial *other) const -{ - Q_ASSERT(other && type() == other->type()); - const QQuickPathItemLinearGradientMaterial *m = static_cast(other); - - QQuickPathItemGenericStrokeFillNode *a = node(); - QQuickPathItemGenericStrokeFillNode *b = m->node(); - Q_ASSERT(a && b); - if (a == b) - return 0; - - const QQuickPathItemGradientCache::GradientDesc *ga = &a->m_fillGradient; - const QQuickPathItemGradientCache::GradientDesc *gb = &b->m_fillGradient; - - if (int d = ga->spread - gb->spread) - return d; - - if (int d = ga->start.x() - gb->start.x()) - return d; - if (int d = ga->start.y() - gb->start.y()) - return d; - if (int d = ga->end.x() - gb->end.x()) - return d; - if (int d = ga->end.y() - gb->end.y()) - return d; - - if (int d = ga->stops.count() - gb->stops.count()) - return d; - - for (int i = 0; i < ga->stops.count(); ++i) { - if (int d = ga->stops[i].first - gb->stops[i].first) - return d; - if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba()) - return d; - } - - return 0; -} - -#endif // QT_NO_OPENGL - -QT_END_NAMESPACE diff --git a/src/quick/items/qquickpathitemgenericrenderer_p.h b/src/quick/items/qquickpathitemgenericrenderer_p.h deleted file mode 100644 index 70a9e88d2f..0000000000 --- a/src/quick/items/qquickpathitemgenericrenderer_p.h +++ /dev/null @@ -1,303 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKPATHITEMGENERICRENDERER_P_H -#define QQUICKPATHITEMGENERICRENDERER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of a number of Qt sources files. This header file may change from -// version to version without notice, or even be removed. -// -// We mean it. -// - -#include "qquickpathitem_p_p.h" -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QQuickPathItemGenericNode; -class QQuickPathItemGenericStrokeFillNode; -class QQuickPathItemFillRunnable; -class QQuickPathItemStrokeRunnable; - -class QQuickPathItemGenericRenderer : public QQuickAbstractPathRenderer -{ -public: - enum Dirty { - DirtyFillGeom = 0x01, - DirtyStrokeGeom = 0x02, - DirtyColor = 0x04, - DirtyFillGradient = 0x08, - DirtyList = 0x10 // only for accDirty - }; - - QQuickPathItemGenericRenderer(QQuickItem *item) - : m_item(item), - m_api(QSGRendererInterface::Unknown), - m_rootNode(nullptr), - m_accDirty(0), - m_asyncCallback(nullptr) - { } - ~QQuickPathItemGenericRenderer(); - - void beginSync(int totalCount) override; - void setPath(int index, const QQuickPath *path) override; - void setJSPath(int index, const QQuickPathItemPath &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 &dashPattern) override; - void setFillGradient(int index, QQuickPathGradient *gradient) override; - void endSync(bool async) override; - void setAsyncCallback(void (*)(void *), void *) override; - Flags flags() const override { return SupportsAsync; } - - void updateNode() override; - - void setRootNode(QQuickPathItemGenericNode *node); - - struct Color4ub { unsigned char r, g, b, a; }; - typedef QVector VertexContainerType; - typedef QVector IndexContainerType; - - static void triangulateFill(const QPainterPath &path, - const Color4ub &fillColor, - VertexContainerType *fillVertices, - IndexContainerType *fillIndices, - QSGGeometry::Type *indexType, - bool supportsElementIndexUint); - static void triangulateStroke(const QPainterPath &path, - const QPen &pen, - const Color4ub &strokeColor, - VertexContainerType *strokeVertices, - const QSize &clipSize); - -private: - void maybeUpdateAsyncItem(); - - struct VisualPathData { - float strokeWidth; - QPen pen; - Color4ub strokeColor; - Color4ub fillColor; - Qt::FillRule fillRule; - QPainterPath path; - bool fillGradientActive; - QQuickPathItemGradientCache::GradientDesc fillGradient; - VertexContainerType fillVertices; - IndexContainerType fillIndices; - QSGGeometry::Type indexType; - VertexContainerType strokeVertices; - int syncDirty; - int effectiveDirty = 0; - QQuickPathItemFillRunnable *pendingFill = nullptr; - QQuickPathItemStrokeRunnable *pendingStroke = nullptr; - }; - - void updateShadowDataInNode(VisualPathData *d, QQuickPathItemGenericStrokeFillNode *n); - void updateFillNode(VisualPathData *d, QQuickPathItemGenericNode *node); - void updateStrokeNode(VisualPathData *d, QQuickPathItemGenericNode *node); - - QQuickItem *m_item; - QSGRendererInterface::GraphicsApi m_api; - QQuickPathItemGenericNode *m_rootNode; - QVector m_vp; - int m_accDirty; - void (*m_asyncCallback)(void *); - void *m_asyncCallbackData; -}; - -class QQuickPathItemFillRunnable : public QObject, public QRunnable -{ - Q_OBJECT - -public: - void run() override; - - bool orphaned = false; - - // input - QPainterPath path; - QQuickPathItemGenericRenderer::Color4ub fillColor; - bool supportsElementIndexUint; - - // output - QQuickPathItemGenericRenderer::VertexContainerType fillVertices; - QQuickPathItemGenericRenderer::IndexContainerType fillIndices; - QSGGeometry::Type indexType; - -Q_SIGNALS: - void done(QQuickPathItemFillRunnable *self); -}; - -class QQuickPathItemStrokeRunnable : public QObject, public QRunnable -{ - Q_OBJECT - -public: - void run() override; - - bool orphaned = false; - - // input - QPainterPath path; - QPen pen; - QQuickPathItemGenericRenderer::Color4ub strokeColor; - QSize clipSize; - - // output - QQuickPathItemGenericRenderer::VertexContainerType strokeVertices; - -Q_SIGNALS: - void done(QQuickPathItemStrokeRunnable *self); -}; - -class QQuickPathItemGenericStrokeFillNode : public QSGGeometryNode -{ -public: - QQuickPathItemGenericStrokeFillNode(QQuickWindow *window); - ~QQuickPathItemGenericStrokeFillNode(); - - enum Material { - MatSolidColor, - MatLinearGradient - }; - - void activateMaterial(Material m); - - QQuickWindow *window() const { return m_window; } - - // shadow data for custom materials - QQuickPathItemGradientCache::GradientDesc m_fillGradient; - -private: - QSGGeometry *m_geometry; - QQuickWindow *m_window; - QSGMaterial *m_material; - QScopedPointer m_solidColorMaterial; - QScopedPointer m_linearGradientMaterial; - - friend class QQuickPathItemGenericRenderer; -}; - -class QQuickPathItemGenericNode : public QSGNode -{ -public: - 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, QQuickPathItemGenericStrokeFillNode *node); -}; - -#ifndef QT_NO_OPENGL - -class QQuickPathItemLinearGradientShader : public QSGMaterialShader -{ -public: - QQuickPathItemLinearGradientShader(); - - void initialize() override; - void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; - char const *const *attributeNames() const override; - - static QSGMaterialType type; - -private: - int m_opacityLoc; - int m_matrixLoc; - int m_gradStartLoc; - int m_gradEndLoc; -}; - -class QQuickPathItemLinearGradientMaterial : public QSGMaterial -{ -public: - QQuickPathItemLinearGradientMaterial(QQuickPathItemGenericStrokeFillNode *node) - : m_node(node) - { - // Passing RequiresFullMatrix is essential in order to prevent the - // batch renderer from baking in simple, translate-only transforms into - // the vertex data. The shader will rely on the fact that - // vertexCoord.xy is the PathItem-space coordinate and so no modifications - // are welcome. - setFlag(Blending | RequiresFullMatrix); - } - - QSGMaterialType *type() const override - { - return &QQuickPathItemLinearGradientShader::type; - } - - int compare(const QSGMaterial *other) const override; - - QSGMaterialShader *createShader() const override - { - return new QQuickPathItemLinearGradientShader; - } - - QQuickPathItemGenericStrokeFillNode *node() const { return m_node; } - -private: - QQuickPathItemGenericStrokeFillNode *m_node; -}; - -#endif // QT_NO_OPENGL - -QT_END_NAMESPACE - -#endif // QQUICKPATHITEMGENERICRENDERER_P_H diff --git a/src/quick/items/qquickpathitemnvprrenderer.cpp b/src/quick/items/qquickpathitemnvprrenderer.cpp deleted file mode 100644 index f8504f9985..0000000000 --- a/src/quick/items/qquickpathitemnvprrenderer.cpp +++ /dev/null @@ -1,923 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquickpathitemnvprrenderer_p.h" -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -void QQuickPathItemNvprRenderer::beginSync(int totalCount) -{ - if (m_vp.count() != totalCount) { - m_vp.resize(totalCount); - m_accDirty |= DirtyList; - } -} - -void QQuickPathItemNvprRenderer::setPath(int index, const QQuickPath *path) -{ - VisualPathGuiData &d(m_vp[index]); - convertPath(path, &d); - d.dirty |= DirtyPath; - m_accDirty |= DirtyPath; -} - -void QQuickPathItemNvprRenderer::setJSPath(int index, const QQuickPathItemPath &path) -{ - VisualPathGuiData &d(m_vp[index]); - convertJSPath(path, &d); - d.dirty |= DirtyPath; - m_accDirty |= DirtyPath; -} - -void QQuickPathItemNvprRenderer::setStrokeColor(int index, const QColor &color) -{ - VisualPathGuiData &d(m_vp[index]); - d.strokeColor = color; - d.dirty |= DirtyStyle; - m_accDirty |= DirtyStyle; -} - -void QQuickPathItemNvprRenderer::setStrokeWidth(int index, qreal w) -{ - VisualPathGuiData &d(m_vp[index]); - d.strokeWidth = w; - d.dirty |= DirtyStyle; - m_accDirty |= DirtyStyle; -} - -void QQuickPathItemNvprRenderer::setFillColor(int index, const QColor &color) -{ - VisualPathGuiData &d(m_vp[index]); - d.fillColor = color; - d.dirty |= DirtyStyle; - m_accDirty |= DirtyStyle; -} - -void QQuickPathItemNvprRenderer::setFillRule(int index, QQuickVisualPath::FillRule fillRule) -{ - VisualPathGuiData &d(m_vp[index]); - d.fillRule = fillRule; - d.dirty |= DirtyFillRule; - m_accDirty |= DirtyFillRule; -} - -void QQuickPathItemNvprRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) -{ - VisualPathGuiData &d(m_vp[index]); - d.joinStyle = joinStyle; - d.miterLimit = miterLimit; - d.dirty |= DirtyStyle; - m_accDirty |= DirtyStyle; -} - -void QQuickPathItemNvprRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) -{ - VisualPathGuiData &d(m_vp[index]); - d.capStyle = capStyle; - d.dirty |= DirtyStyle; - m_accDirty |= DirtyStyle; -} - -void QQuickPathItemNvprRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, - qreal dashOffset, const QVector &dashPattern) -{ - 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(int index, QQuickPathGradient *gradient) -{ - VisualPathGuiData &d(m_vp[index]); - d.fillGradientActive = gradient != nullptr; - if (gradient) { - d.fillGradient.stops = gradient->sortedGradientStops(); - d.fillGradient.spread = gradient->spread(); - if (QQuickPathLinearGradient *g = qobject_cast(gradient)) { - d.fillGradient.start = QPointF(g->x1(), g->y1()); - d.fillGradient.end = QPointF(g->x2(), g->y2()); - } else { - Q_UNREACHABLE(); - } - } - d.dirty |= DirtyFillGradient; - m_accDirty |= DirtyFillGradient; -} - -void QQuickPathItemNvprRenderer::endSync(bool) -{ -} - -void QQuickPathItemNvprRenderer::setNode(QQuickPathItemNvprRenderNode *node) -{ - if (m_node != node) { - m_node = node; - m_accDirty |= DirtyList; - } -} - -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) { - static struct { GLubyte cmd; const char *s; int coordCount; } nameTab[] = { - { GL_MOVE_TO_NV, "moveTo", 2 }, - { GL_LINE_TO_NV, "lineTo", 2 }, - { GL_QUADRATIC_CURVE_TO_NV, "quadTo", 4 }, - { GL_CUBIC_CURVE_TO_NV, "cubicTo", 6 }, - { GL_LARGE_CW_ARC_TO_NV, "arcTo-large-CW", 5 }, - { GL_LARGE_CCW_ARC_TO_NV, "arcTo-large-CCW", 5 }, - { GL_SMALL_CW_ARC_TO_NV, "arcTo-small-CW", 5 }, - { GL_SMALL_CCW_ARC_TO_NV, "arcTo-small-CCW", 5 }, - { GL_CLOSE_PATH_NV, "closePath", 0 } }; - for (size_t i = 0; i < sizeof(nameTab) / sizeof(nameTab[0]); ++i) { - if (nameTab[i].cmd == cmd) { - QByteArray cs; - for (int j = 0; j < nameTab[i].coordCount; ++j) { - cs.append(QByteArray::number(path.coord[ci++])); - cs.append(' '); - } - debug << "\n " << nameTab[i].s << " " << cs; - break; - } - } - } - return debug; -} - -static inline void appendCoords(QVector *v, QQuickCurve *c, QPointF *pos) -{ - QPointF p(c->hasRelativeX() ? pos->x() + c->relativeX() : c->x(), - c->hasRelativeY() ? pos->y() + c->relativeY() : c->y()); - v->append(p.x()); - v->append(p.y()); - *pos = p; -} - -static inline void appendControlCoords(QVector *v, QQuickPathQuad *c, const QPointF &pos) -{ - QPointF p(c->hasRelativeControlX() ? pos.x() + c->relativeControlX() : c->controlX(), - c->hasRelativeControlY() ? pos.y() + c->relativeControlY() : c->controlY()); - v->append(p.x()); - v->append(p.y()); -} - -static inline void appendControl1Coords(QVector *v, QQuickPathCubic *c, const QPointF &pos) -{ - QPointF p(c->hasRelativeControl1X() ? pos.x() + c->relativeControl1X() : c->control1X(), - c->hasRelativeControl1Y() ? pos.y() + c->relativeControl1Y() : c->control1Y()); - v->append(p.x()); - v->append(p.y()); -} - -static inline void appendControl2Coords(QVector *v, QQuickPathCubic *c, const QPointF &pos) -{ - QPointF p(c->hasRelativeControl2X() ? pos.x() + c->relativeControl2X() : c->control2X(), - c->hasRelativeControl2Y() ? pos.y() + c->relativeControl2Y() : c->control2Y()); - v->append(p.x()); - v->append(p.y()); -} - -void QQuickPathItemNvprRenderer::convertPath(const QQuickPath *path, VisualPathGuiData *d) -{ - d->path = NvprPath(); - if (!path) - return; - - const QList &pp(QQuickPathPrivate::get(path)->_pathElements); - if (pp.isEmpty()) - return; - - 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(e)) { - d->path.cmd.append(GL_MOVE_TO_NV); - appendCoords(&d->path.coord, o, &pos); - startPos = pos; - } else if (QQuickPathLine *o = qobject_cast(e)) { - d->path.cmd.append(GL_LINE_TO_NV); - appendCoords(&d->path.coord, o, &pos); - } else if (QQuickPathQuad *o = qobject_cast(e)) { - 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(e)) { - 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(e)) { - const bool sweepFlag = o->direction() == QQuickPathArc::Clockwise; // maps to CCW, not a typo - GLenum cmd; - if (o->useLargeArc()) - cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV; - else - cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV; - 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(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; - } - } - - // 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); -} - -void QQuickPathItemNvprRenderer::convertJSPath(const QQuickPathItemPath &path, VisualPathGuiData *d) -{ - d->path = NvprPath(); - if (path.cmd.isEmpty()) - return; - - QPointF startPos(0, 0); - QPointF pos(startPos); - int coordIdx = 0; - - for (QQuickPathItemPath::Command cmd : path.cmd) { - switch (cmd) { - case QQuickPathItemPath::MoveTo: - d->path.cmd.append(GL_MOVE_TO_NV); - pos = QPointF(path.coords[coordIdx], path.coords[coordIdx + 1]); - startPos = pos; - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - coordIdx += 2; - break; - case QQuickPathItemPath::LineTo: - d->path.cmd.append(GL_LINE_TO_NV); - pos = QPointF(path.coords[coordIdx], path.coords[coordIdx + 1]); - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - coordIdx += 2; - break; - case QQuickPathItemPath::QuadTo: - d->path.cmd.append(GL_QUADRATIC_CURVE_TO_NV); - d->path.coord.append(path.coords[coordIdx]); - d->path.coord.append(path.coords[coordIdx + 1]); - pos = QPointF(path.coords[coordIdx + 2], path.coords[coordIdx + 3]); - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - coordIdx += 4; - break; - case QQuickPathItemPath::CubicTo: - d->path.cmd.append(GL_CUBIC_CURVE_TO_NV); - d->path.coord.append(path.coords[coordIdx]); - d->path.coord.append(path.coords[coordIdx + 1]); - d->path.coord.append(path.coords[coordIdx + 2]); - d->path.coord.append(path.coords[coordIdx + 3]); - pos = QPointF(path.coords[coordIdx + 4], path.coords[coordIdx + 5]); - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - coordIdx += 6; - break; - case QQuickPathItemPath::ArcTo: - { - const bool sweepFlag = !qFuzzyIsNull(path.coords[coordIdx + 5]); - const bool useLargeArc = !qFuzzyIsNull(path.coords[coordIdx + 6]); - GLenum cmd; - if (useLargeArc) - cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV; - else - cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV; - d->path.cmd.append(cmd); - d->path.coord.append(path.coords[coordIdx]); // rx - d->path.coord.append(path.coords[coordIdx + 1]); // ry - d->path.coord.append(path.coords[coordIdx + 2]); // xrot - pos = QPointF(path.coords[coordIdx + 3], path.coords[coordIdx + 4]); - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - coordIdx += 7; - } - break; - default: - qWarning("Unknown JS path command: %d", cmd); - break; - } - } - - if (pos == startPos) - d->path.cmd.append(GL_CLOSE_PATH_NV); -} - -static inline QVector4D qsg_premultiply(const QColor &c, float globalOpacity) -{ - const float o = c.alphaF() * globalOpacity; - return QVector4D(c.redF() * o, c.greenF() * o, c.blueF() * o, o); -} - -void QQuickPathItemNvprRenderer::updateNode() -{ - // Called on the render thread with gui blocked -> update the node with its - // own copy of all relevant data. - - if (!m_accDirty) - return; - - 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 (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 (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 (dirty & DirtyFillGradient) { - dst.fillGradientActive = src.fillGradientActive; - if (src.fillGradientActive) - dst.fillGradient = src.fillGradient; - } - } - - m_node->markDirty(QSGNode::DirtyMaterial); - m_accDirty = 0; -} - -bool QQuickPathItemNvprRenderNode::nvprInited = false; -QQuickNvprFunctions QQuickPathItemNvprRenderNode::nvpr; -QQuickNvprMaterialManager QQuickPathItemNvprRenderNode::mtlmgr; - -QQuickPathItemNvprRenderNode::~QQuickPathItemNvprRenderNode() -{ - releaseResources(); -} - -void QQuickPathItemNvprRenderNode::releaseResources() -{ - 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(); -} - -void QQuickNvprMaterialManager::create(QQuickNvprFunctions *nvpr) -{ - m_nvpr = nvpr; -} - -void QQuickNvprMaterialManager::releaseResources() -{ - QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); - for (MaterialDesc &mtl : m_materials) { - if (mtl.ppl) { - f->glDeleteProgramPipelines(1, &mtl.ppl); - mtl = MaterialDesc(); - } - } -} - -QQuickNvprMaterialManager::MaterialDesc *QQuickNvprMaterialManager::activateMaterial(Material m) -{ - QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); - MaterialDesc &mtl(m_materials[m]); - - if (!mtl.ppl) { - if (m == MatSolid) { - static const char *fragSrc = - "#version 310 es\n" - "precision highp float;\n" - "out vec4 fragColor;\n" - "uniform vec4 color;\n" - "uniform float opacity;\n" - "void main() {\n" - " fragColor = color * opacity;\n" - "}\n"; - if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { - qWarning("NVPR: Failed to create shader pipeline for solid fill"); - return nullptr; - } - Q_ASSERT(mtl.ppl && mtl.prg); - mtl.uniLoc[0] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "color"); - Q_ASSERT(mtl.uniLoc[0] >= 0); - mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); - Q_ASSERT(mtl.uniLoc[1] >= 0); - } else if (m == MatLinearGradient) { - static const char *fragSrc = - "#version 310 es\n" - "precision highp float;\n" - "layout(location = 0) in vec2 uv;" - "uniform float opacity;\n" - "uniform sampler2D gradTab;\n" - "uniform vec2 gradStart;\n" - "uniform vec2 gradEnd;\n" - "out vec4 fragColor;\n" - "void main() {\n" - " vec2 gradVec = gradEnd - gradStart;\n" - " float gradTabIndex = dot(gradVec, uv - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y);\n" - " fragColor = texture(gradTab, vec2(gradTabIndex, 0.5)) * opacity;\n" - "}\n"; - if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { - qWarning("NVPR: Failed to create shader pipeline for linear gradient"); - return nullptr; - } - Q_ASSERT(mtl.ppl && mtl.prg); - mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); - Q_ASSERT(mtl.uniLoc[1] >= 0); - mtl.uniLoc[2] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradStart"); - Q_ASSERT(mtl.uniLoc[2] >= 0); - mtl.uniLoc[3] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradEnd"); - Q_ASSERT(mtl.uniLoc[3] >= 0); - } else { - Q_UNREACHABLE(); - } - } - - f->glBindProgramPipeline(mtl.ppl); - - return &mtl; -} - -void QQuickPathItemNvprRenderNode::updatePath(VisualPathRenderData *d) -{ - 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()); - } - } - - 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 (d->dirty & QQuickPathItemNvprRenderer::DirtyDash) { - nvpr.pathParameterf(d->path, GL_PATH_DASH_OFFSET_NV, d->dashOffset); - // count == 0 -> no dash - nvpr.pathDashArray(d->path, d->dashPattern.count(), d->dashPattern.constData()); - } - - if (d->dirty) - d->fallbackValid = false; -} - -void QQuickPathItemNvprRenderNode::renderStroke(VisualPathRenderData *d, int strokeStencilValue, int writeMask) -{ - QQuickNvprMaterialManager::MaterialDesc *mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); - f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], - d->strokeColor.x(), d->strokeColor.y(), d->strokeColor.z(), d->strokeColor.w()); - f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); - - nvpr.stencilThenCoverStrokePath(d->path, strokeStencilValue, writeMask, GL_CONVEX_HULL_NV); -} - -void QQuickPathItemNvprRenderNode::renderFill(VisualPathRenderData *d) -{ - QQuickNvprMaterialManager::MaterialDesc *mtl = nullptr; - if (d->fillGradientActive) { - mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatLinearGradient); - 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 - // we need since the gradient's start and stop are in that space too. - GLfloat coeff[6] = { 1, 0, 0, - 0, 1, 0 }; - nvpr.programPathFragmentInputGen(mtl->prg, 0, GL_OBJECT_LINEAR_NV, 2, coeff); - f->glProgramUniform2f(mtl->prg, mtl->uniLoc[2], 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], - 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(d->path, d->fillRule, writeMask, GL_BOUNDING_BOX_NV); -} - -void QQuickPathItemNvprRenderNode::renderOffscreenFill(VisualPathRenderData *d) -{ - if (d->fallbackValid && d->fallbackFbo) - return; - - GLfloat bb[4]; - nvpr.getPathParameterfv(d->path, GL_PATH_STROKE_BOUNDING_BOX_NV, bb); - QSize sz = QSizeF(bb[2] - bb[0] + 1, bb[3] - bb[1] + 1).toSize(); - d->fallbackSize = QSize(qMax(32, sz.width()), qMax(32, sz.height())); - d->fallbackTopLeft = QPointF(bb[0], bb[1]); - - if (d->fallbackFbo && d->fallbackFbo->size() != d->fallbackSize) { - delete d->fallbackFbo; - d->fallbackFbo = nullptr; - } - if (!d->fallbackFbo) - d->fallbackFbo = new QOpenGLFramebufferObject(d->fallbackSize, QOpenGLFramebufferObject::CombinedDepthStencil); - if (!d->fallbackFbo->bind()) - return; - - GLint prevViewport[4]; - f->glGetIntegerv(GL_VIEWPORT, prevViewport); - - f->glViewport(0, 0, d->fallbackSize.width(), d->fallbackSize.height()); - f->glDisable(GL_DEPTH_TEST); - f->glClearColor(0, 0, 0, 0); - f->glClearStencil(0); - f->glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); - f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - - QMatrix4x4 mv; - mv.translate(-d->fallbackTopLeft.x(), -d->fallbackTopLeft.y()); - nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, mv.constData()); - QMatrix4x4 proj; - proj.ortho(0, d->fallbackSize.width(), d->fallbackSize.height(), 0, 1, -1); - nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, proj.constData()); - - renderFill(d); - - d->fallbackFbo->release(); - f->glEnable(GL_DEPTH_TEST); - f->glViewport(prevViewport[0], prevViewport[1], prevViewport[2], prevViewport[3]); - - d->fallbackValid = true; -} - -void QQuickPathItemNvprRenderNode::setupStencilForCover(bool stencilClip, int sv) -{ - if (!stencilClip) { - // Assume stencil buffer is cleared to 0 for each frame. - // Within the frame dppass=GL_ZERO for glStencilOp ensures stencil is reset and so no need to clear. - f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); - f->glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO); - } else { - f->glStencilFunc(GL_LESS, sv, 0xFF); // pass if (sv & 0xFF) < (stencil_value & 0xFF) - f->glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); // dppass: replace with the original value (clip's stencil ref value) - } -} - -void QQuickPathItemNvprRenderNode::render(const RenderState *state) -{ - f = QOpenGLContext::currentContext()->extraFunctions(); - - if (!nvprInited) { - if (!nvpr.create()) { - qWarning("NVPR init failed"); - return; - } - mtlmgr.create(&nvpr); - nvprInited = true; - } - - f->glUseProgram(0); - f->glStencilMask(~0); - f->glEnable(GL_STENCIL_TEST); - - const bool stencilClip = state->stencilEnabled(); - // when true, the stencil buffer already has a clip path with a ref value of sv - const int sv = state->stencilValue(); - const bool hasScissor = state->scissorEnabled(); - - if (hasScissor) { - // scissor rect is already set, just enable scissoring - f->glEnable(GL_SCISSOR_TEST); - } - - // 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); - - bool reloadMatrices = true; - - for (VisualPathRenderData &d : m_vp) { - updatePath(&d); - - const bool hasFill = d.hasFill(); - const bool hasStroke = d.hasStroke(); - - 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... - if (hasScissor) - f->glDisable(GL_SCISSOR_TEST); - renderOffscreenFill(&d); - reloadMatrices = true; - if (hasScissor) - f->glEnable(GL_SCISSOR_TEST); - } - - if (reloadMatrices) { - reloadMatrices = false; - nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, matrix()->constData()); - nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, state->projectionMatrix()->constData()); - } - - // 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); - QMatrix4x4 mv = *matrix(); - mv.translate(d.fallbackTopLeft.x(), d.fallbackTopLeft.y()); - m_fallbackBlitter.texturedQuad(d.fallbackFbo->texture(), d.fallbackFbo->size(), - *state->projectionMatrix(), mv, - 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); - } - - renderStroke(&d, strokeStencilValue, writeMask); - } - - if (stencilClip) - nvpr.pathStencilFunc(GL_ALWAYS, 0, ~0); - - d.dirty = 0; - } - - f->glBindProgramPipeline(0); -} - -QSGRenderNode::StateFlags QQuickPathItemNvprRenderNode::changedStates() const -{ - return BlendState | StencilState | DepthState | ScissorState; -} - -QSGRenderNode::RenderingFlags QQuickPathItemNvprRenderNode::flags() const -{ - return DepthAwareRendering; // avoid hitting the less optimal no-opaque-batch path in the renderer -} - -bool QQuickPathItemNvprRenderNode::isSupported() -{ - static const bool nvprDisabled = qEnvironmentVariableIntValue("QT_NO_NVPR") != 0; - return !nvprDisabled && QQuickNvprFunctions::isSupported(); -} - -bool QQuickNvprBlitter::create() -{ - if (isCreated()) - destroy(); - - m_program = new QOpenGLShaderProgram; - if (QOpenGLContext::currentContext()->format().profile() == QSurfaceFormat::CoreProfile) { - m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/shadereffect_core.vert")); - m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/shadereffect_core.frag")); - } else { - m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/shadereffect.vert")); - m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/shadereffect.frag")); - } - m_program->bindAttributeLocation("qt_Vertex", 0); - m_program->bindAttributeLocation("qt_MultiTexCoord0", 1); - if (!m_program->link()) - return false; - - m_matrixLoc = m_program->uniformLocation("qt_Matrix"); - m_opacityLoc = m_program->uniformLocation("qt_Opacity"); - - m_buffer = new QOpenGLBuffer; - if (!m_buffer->create()) - return false; - m_buffer->bind(); - m_buffer->allocate(4 * sizeof(GLfloat) * 6); - m_buffer->release(); - - return true; -} - -void QQuickNvprBlitter::destroy() -{ - if (m_program) { - delete m_program; - m_program = nullptr; - } - if (m_buffer) { - delete m_buffer; - m_buffer = nullptr; - } -} - -void QQuickNvprBlitter::texturedQuad(GLuint textureId, const QSize &size, - const QMatrix4x4 &proj, const QMatrix4x4 &modelview, - float opacity) -{ - QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); - - m_program->bind(); - - QMatrix4x4 m = proj * modelview; - m_program->setUniformValue(m_matrixLoc, m); - m_program->setUniformValue(m_opacityLoc, opacity); - - m_buffer->bind(); - - if (size != m_prevSize) { - m_prevSize = size; - - QPointF p0(size.width() - 1, size.height() - 1); - QPointF p1(0, 0); - QPointF p2(0, size.height() - 1); - QPointF p3(size.width() - 1, 0); - - GLfloat vertices[6 * 4] = { - GLfloat(p0.x()), GLfloat(p0.y()), 1, 0, - GLfloat(p1.x()), GLfloat(p1.y()), 0, 1, - GLfloat(p2.x()), GLfloat(p2.y()), 0, 0, - - GLfloat(p0.x()), GLfloat(p0.y()), 1, 0, - GLfloat(p3.x()), GLfloat(p3.y()), 1, 1, - GLfloat(p1.x()), GLfloat(p1.y()), 0, 1, - }; - - m_buffer->write(0, vertices, sizeof(vertices)); - } - - m_program->enableAttributeArray(0); - m_program->enableAttributeArray(1); - f->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0); - f->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (const void *) (2 * sizeof(GLfloat))); - - f->glBindTexture(GL_TEXTURE_2D, textureId); - - f->glDrawArrays(GL_TRIANGLES, 0, 6); - - f->glBindTexture(GL_TEXTURE_2D, 0); - m_buffer->release(); - m_program->release(); -} - -QT_END_NAMESPACE diff --git a/src/quick/items/qquickpathitemnvprrenderer_p.h b/src/quick/items/qquickpathitemnvprrenderer_p.h deleted file mode 100644 index deab9cf7f9..0000000000 --- a/src/quick/items/qquickpathitemnvprrenderer_p.h +++ /dev/null @@ -1,237 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKPATHITEMNVPRRENDERER_P_H -#define QQUICKPATHITEMNVPRRENDERER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of a number of Qt sources files. This header file may change from -// version to version without notice, or even be removed. -// -// We mean it. -// - -#include "qquickpathitem_p_p.h" -#include -#include -#include -#include -#include - -#ifndef QT_NO_OPENGL - -QT_BEGIN_NAMESPACE - -class QQuickPathItemNvprRenderNode; -class QOpenGLFramebufferObject; -class QOpenGLBuffer; -class QOpenGLExtraFunctions; - -class QQuickPathItemNvprRenderer : public QQuickAbstractPathRenderer -{ -public: - enum Dirty { - DirtyPath = 0x01, - DirtyStyle = 0x02, - DirtyFillRule = 0x04, - DirtyDash = 0x08, - DirtyFillGradient = 0x10, - DirtyList = 0x20 - }; - - void beginSync(int totalCount) override; - void setPath(int index, const QQuickPath *path) override; - void setJSPath(int index, const QQuickPathItemPath &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 &dashPattern) override; - void setFillGradient(int index, QQuickPathGradient *gradient) override; - void endSync(bool async) override; - - void updateNode() override; - - void setNode(QQuickPathItemNvprRenderNode *node); - - struct NvprPath { - QVector cmd; - QVector coord; - QByteArray str; - }; - -private: - 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 dashPattern; - bool fillGradientActive; - QQuickPathItemGradientCache::GradientDesc fillGradient; - }; - - void convertPath(const QQuickPath *path, VisualPathGuiData *d); - void convertJSPath(const QQuickPathItemPath &path, VisualPathGuiData *d); - - QQuickPathItemNvprRenderNode *m_node = nullptr; - int m_accDirty = 0; - - QVector m_vp; -}; - -QDebug operator<<(QDebug debug, const QQuickPathItemNvprRenderer::NvprPath &path); - -class QQuickNvprMaterialManager -{ -public: - enum Material { - MatSolid, - MatLinearGradient, - - NMaterials - }; - - struct MaterialDesc { - GLuint ppl = 0; - GLuint prg = 0; - int uniLoc[4]; - }; - - void create(QQuickNvprFunctions *nvpr); - MaterialDesc *activateMaterial(Material m); - void releaseResources(); - -private: - QQuickNvprFunctions *m_nvpr; - MaterialDesc m_materials[NMaterials]; -}; - -class QQuickNvprBlitter -{ -public: - bool create(); - void destroy(); - bool isCreated() const { return m_program != nullptr; } - void texturedQuad(GLuint textureId, const QSize &size, - const QMatrix4x4 &proj, const QMatrix4x4 &modelview, - float opacity); - -private: - QOpenGLShaderProgram *m_program = nullptr; - QOpenGLBuffer *m_buffer = nullptr; - int m_matrixLoc; - int m_opacityLoc; - QSize m_prevSize; -}; - -class QQuickPathItemNvprRenderNode : public QSGRenderNode -{ -public: - ~QQuickPathItemNvprRenderNode(); - - void render(const RenderState *state) override; - void releaseResources() override; - StateFlags changedStates() const override; - RenderingFlags flags() const override; - - static bool isSupported(); - -private: - 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 dashPattern; - bool fillGradientActive; - QQuickPathItemGradientCache::GradientDesc fillGradient; - QOpenGLFramebufferObject *fallbackFbo = nullptr; - bool fallbackValid = false; - QSize fallbackSize; - QPointF fallbackTopLeft; - - bool hasFill() const { return !qFuzzyIsNull(fillColor.w()) || fillGradientActive; } - bool hasStroke() const { return strokeWidth >= 0.0f && !qFuzzyIsNull(strokeColor.w()); } - }; - - 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; - static QQuickNvprFunctions nvpr; - static QQuickNvprMaterialManager mtlmgr; - - QQuickNvprBlitter m_fallbackBlitter; - QOpenGLExtraFunctions *f = nullptr; - - QVector m_vp; - - friend class QQuickPathItemNvprRenderer; -}; - -QT_END_NAMESPACE - -#endif // QT_NO_OPENGL - -#endif // QQUICKPATHITEMNVPRRENDERER_P_H diff --git a/src/quick/items/qquickpathitemsoftwarerenderer.cpp b/src/quick/items/qquickpathitemsoftwarerenderer.cpp deleted file mode 100644 index b7aa93bf65..0000000000 --- a/src/quick/items/qquickpathitemsoftwarerenderer.cpp +++ /dev/null @@ -1,277 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquickpathitemsoftwarerenderer_p.h" -#include - -QT_BEGIN_NAMESPACE - -void QQuickPathItemSoftwareRenderer::beginSync(int totalCount) -{ - if (m_vp.count() != totalCount) { - m_vp.resize(totalCount); - m_accDirty |= DirtyList; - } -} - -void QQuickPathItemSoftwareRenderer::setPath(int index, const QQuickPath *path) -{ - VisualPathGuiData &d(m_vp[index]); - d.path = path ? path->path() : QPainterPath(); - d.dirty |= DirtyPath; - m_accDirty |= DirtyPath; -} - -void QQuickPathItemSoftwareRenderer::setJSPath(int index, const QQuickPathItemPath &path) -{ - VisualPathGuiData &d(m_vp[index]); - d.path = path.toPainterPath(); - d.dirty |= DirtyPath; - m_accDirty |= DirtyPath; -} - -void QQuickPathItemSoftwareRenderer::setStrokeColor(int index, const QColor &color) -{ - VisualPathGuiData &d(m_vp[index]); - d.pen.setColor(color); - d.dirty |= DirtyPen; - m_accDirty |= DirtyPen; -} - -void QQuickPathItemSoftwareRenderer::setStrokeWidth(int index, qreal w) -{ - VisualPathGuiData &d(m_vp[index]); - d.strokeWidth = w; - if (w >= 0.0f) - d.pen.setWidthF(w); - d.dirty |= DirtyPen; - m_accDirty |= DirtyPen; -} - -void QQuickPathItemSoftwareRenderer::setFillColor(int index, const QColor &color) -{ - VisualPathGuiData &d(m_vp[index]); - d.fillColor = color; - d.brush.setColor(color); - d.dirty |= DirtyBrush; - m_accDirty |= DirtyBrush; -} - -void QQuickPathItemSoftwareRenderer::setFillRule(int index, QQuickVisualPath::FillRule fillRule) -{ - VisualPathGuiData &d(m_vp[index]); - d.fillRule = Qt::FillRule(fillRule); - d.dirty |= DirtyFillRule; - m_accDirty |= DirtyFillRule; -} - -void QQuickPathItemSoftwareRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) -{ - VisualPathGuiData &d(m_vp[index]); - d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); - d.pen.setMiterLimit(miterLimit); - d.dirty |= DirtyPen; - m_accDirty |= DirtyPen; -} - -void QQuickPathItemSoftwareRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) -{ - VisualPathGuiData &d(m_vp[index]); - d.pen.setCapStyle(Qt::PenCapStyle(capStyle)); - d.dirty |= DirtyPen; - m_accDirty |= DirtyPen; -} - -void QQuickPathItemSoftwareRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, - qreal dashOffset, const QVector &dashPattern) -{ - VisualPathGuiData &d(m_vp[index]); - switch (strokeStyle) { - case QQuickVisualPath::SolidLine: - d.pen.setStyle(Qt::SolidLine); - break; - case QQuickVisualPath::DashLine: - d.pen.setStyle(Qt::CustomDashLine); - d.pen.setDashPattern(dashPattern); - d.pen.setDashOffset(dashOffset); - break; - default: - break; - } - d.dirty |= DirtyPen; - m_accDirty |= DirtyPen; -} - -void QQuickPathItemSoftwareRenderer::setFillGradient(int index, QQuickPathGradient *gradient) -{ - VisualPathGuiData &d(m_vp[index]); - if (QQuickPathLinearGradient *linearGradient = qobject_cast(gradient)) { - QLinearGradient painterGradient(linearGradient->x1(), linearGradient->y1(), - linearGradient->x2(), linearGradient->y2()); - painterGradient.setStops(linearGradient->sortedGradientStops()); - switch (gradient->spread()) { - case QQuickPathGradient::PadSpread: - painterGradient.setSpread(QGradient::PadSpread); - break; - case QQuickPathGradient::RepeatSpread: - painterGradient.setSpread(QGradient::RepeatSpread); - break; - case QQuickPathGradient::ReflectSpread: - painterGradient.setSpread(QGradient::ReflectSpread); - break; - default: - break; - } - d.brush = QBrush(painterGradient); - } else { - d.brush = QBrush(d.fillColor); - } - d.dirty |= DirtyBrush; - m_accDirty |= DirtyBrush; -} - -void QQuickPathItemSoftwareRenderer::endSync(bool) -{ -} - -void QQuickPathItemSoftwareRenderer::setNode(QQuickPathItemSoftwareRenderNode *node) -{ - if (m_node != node) { - m_node = node; - m_accDirty |= DirtyList; - } -} - -void QQuickPathItemSoftwareRenderer::updateNode() -{ - if (!m_accDirty) - return; - - const int count = m_vp.count(); - const bool listChanged = m_accDirty & DirtyList; - if (listChanged) - m_node->m_vp.resize(count); - - m_node->m_boundingRect = QRectF(); - - for (int i = 0; i < count; ++i) { - VisualPathGuiData &src(m_vp[i]); - QQuickPathItemSoftwareRenderNode::VisualPathRenderData &dst(m_node->m_vp[i]); - - if (listChanged || (src.dirty & DirtyPath)) { - dst.path = src.path; - dst.path.setFillRule(src.fillRule); - } - - 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; - - src.dirty = 0; - - QRectF br = dst.path.boundingRect(); - const float sw = qMax(1.0f, dst.strokeWidth); - br.adjust(-sw, -sw, sw, sw); - m_node->m_boundingRect |= br; - } - - m_node->markDirty(QSGNode::DirtyMaterial); - m_accDirty = 0; -} - -QQuickPathItemSoftwareRenderNode::QQuickPathItemSoftwareRenderNode(QQuickPathItem *item) - : m_item(item) -{ -} - -QQuickPathItemSoftwareRenderNode::~QQuickPathItemSoftwareRenderNode() -{ - releaseResources(); -} - -void QQuickPathItemSoftwareRenderNode::releaseResources() -{ -} - -void QQuickPathItemSoftwareRenderNode::render(const RenderState *state) -{ - if (m_vp.isEmpty()) - return; - - QSGRendererInterface *rif = m_item->window()->rendererInterface(); - QPainter *p = static_cast(rif->getResource(m_item->window(), QSGRendererInterface::PainterResource)); - Q_ASSERT(p); - - const QRegion *clipRegion = state->clipRegion(); - if (clipRegion && !clipRegion->isEmpty()) - p->setClipRegion(*clipRegion, Qt::ReplaceClip); // must be done before setTransform - - p->setTransform(matrix()->toTransform()); - p->setOpacity(inheritedOpacity()); - - 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 -{ - return 0; -} - -QSGRenderNode::RenderingFlags QQuickPathItemSoftwareRenderNode::flags() const -{ - return BoundedRectRendering; // avoid fullscreen updates by saying we won't draw outside rect() -} - -QRectF QQuickPathItemSoftwareRenderNode::rect() const -{ - return m_boundingRect; -} - -QT_END_NAMESPACE diff --git a/src/quick/items/qquickpathitemsoftwarerenderer_p.h b/src/quick/items/qquickpathitemsoftwarerenderer_p.h deleted file mode 100644 index e76590bdfe..0000000000 --- a/src/quick/items/qquickpathitemsoftwarerenderer_p.h +++ /dev/null @@ -1,136 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKPATHITEMSOFTWARERENDERER_P_H -#define QQUICKPATHITEMSOFTWARERENDERER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of a number of Qt sources files. This header file may change from -// version to version without notice, or even be removed. -// -// We mean it. -// - -#include "qquickpathitem_p_p.h" -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QQuickPathItemSoftwareRenderNode; - -class QQuickPathItemSoftwareRenderer : public QQuickAbstractPathRenderer -{ -public: - enum Dirty { - DirtyPath = 0x01, - DirtyPen = 0x02, - DirtyFillRule = 0x04, - DirtyBrush = 0x08, - DirtyList = 0x10 - }; - - void beginSync(int totalCount) override; - void setPath(int index, const QQuickPath *path) override; - void setJSPath(int index, const QQuickPathItemPath &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 &dashPattern) override; - void setFillGradient(int index, QQuickPathGradient *gradient) override; - void endSync(bool async) override; - - void updateNode() override; - - void setNode(QQuickPathItemSoftwareRenderNode *node); - -private: - QQuickPathItemSoftwareRenderNode *m_node = nullptr; - int m_accDirty = 0; - struct VisualPathGuiData { - int dirty = 0; - QPainterPath path; - QPen pen; - float strokeWidth; - QColor fillColor; - QBrush brush; - Qt::FillRule fillRule; - }; - QVector m_vp; -}; - -class QQuickPathItemSoftwareRenderNode : public QSGRenderNode -{ -public: - QQuickPathItemSoftwareRenderNode(QQuickPathItem *item); - ~QQuickPathItemSoftwareRenderNode(); - - void render(const RenderState *state) override; - void releaseResources() override; - StateFlags changedStates() const override; - RenderingFlags flags() const override; - QRectF rect() const override; - -private: - QQuickPathItem *m_item; - - struct VisualPathRenderData { - QPainterPath path; - QPen pen; - float strokeWidth; - QBrush brush; - }; - QVector m_vp; - QRectF m_boundingRect; - - friend class QQuickPathItemSoftwareRenderer; -}; - -QT_END_NAMESPACE - -#endif // QQUICKPATHITEMSOFTWARERENDERER_P_H diff --git a/src/quick/util/qquicknvprfunctions.cpp b/src/quick/util/qquicknvprfunctions.cpp deleted file mode 100644 index 40eb2bb932..0000000000 --- a/src/quick/util/qquicknvprfunctions.cpp +++ /dev/null @@ -1,284 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquicknvprfunctions_p.h" - -#ifndef QT_NO_OPENGL - -#include -#include -#include -#include "qquicknvprfunctions_p_p.h" - -QT_BEGIN_NAMESPACE - -/*! - \class QQuickNvprFunctions - - \brief Function resolvers and other helpers for GL_NV_path_rendering - for both desktop (GL 4.3+) and mobile/embedded (GLES 3.1+) in a manner - that does not distract builds that do not have NVPR support either at - compile or run time. - - \internal - */ - -QQuickNvprFunctions::QQuickNvprFunctions() - : d(new QQuickNvprFunctionsPrivate(this)) -{ -} - -QQuickNvprFunctions::~QQuickNvprFunctions() -{ - delete d; -} - -/*! - \return a recommended QSurfaceFormat suitable for GL_NV_path_rendering on top - of OpenGL 4.3 or OpenGL ES 3.1. - */ -QSurfaceFormat QQuickNvprFunctions::format() -{ - QSurfaceFormat fmt; - fmt.setDepthBufferSize(24); - fmt.setStencilBufferSize(8); - if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { - fmt.setVersion(4, 3); - fmt.setProfile(QSurfaceFormat::CompatibilityProfile); - } else if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { - fmt.setVersion(3, 1); - } - return fmt; -} - -/*! - \return true if GL_NV_path_rendering is supported with the current OpenGL - context. - - When there is no current context, a temporary dummy one will be created and - made current. - */ -bool QQuickNvprFunctions::isSupported() -{ - QOpenGLContext *ctx = QOpenGLContext::currentContext(); - QScopedPointer tempContext; - QScopedPointer tempSurface; - if (!ctx) { - tempContext.reset(new QOpenGLContext); - if (!tempContext->create()) - return false; - ctx = tempContext.data(); - tempSurface.reset(new QOffscreenSurface); - tempSurface->setFormat(ctx->format()); - tempSurface->create(); - if (!ctx->makeCurrent(tempSurface.data())) - return false; - } - - if (!ctx->hasExtension(QByteArrayLiteral("GL_NV_path_rendering"))) - return false; - - // Do not check for DSA as the string may not be exposed on ES - // drivers, yet the functions we need are resolvable. -#if 0 - if (!ctx->hasExtension(QByteArrayLiteral("GL_EXT_direct_state_access"))) { - qWarning("QtQuickPath/NVPR: GL_EXT_direct_state_access not supported"); - return false; - } -#endif - - return true; -} - -/*! - Initializes using the current OpenGL context. - - \return true when GL_NV_path_rendering is supported and initialization was - successful. - */ -bool QQuickNvprFunctions::create() -{ - return isSupported() && d->resolve(); -} - -/*! - Creates a program pipeline consisting of a separable fragment shader program. - - This is essential for using NVPR with OpenGL ES 3.1+ since normal, - GLES2-style programs would not work without a vertex shader. - - \note \a fragmentShaderSource should be a \c{version 310 es} shader since - this works both on desktop and embedded NVIDIA drivers, thus avoiding the - need to fight GLSL and GLSL ES differences. - - The pipeline object is stored into \a pipeline, the fragment shader program - into \a program. - - Use QOpenGLExtraFunctions to set uniforms, bind the pipeline, etc. - - \return \c false on failure in which case the error log is printed on the - debug output. \c true on success. - */ -bool QQuickNvprFunctions::createFragmentOnlyPipeline(const char *fragmentShaderSource, GLuint *pipeline, GLuint *program) -{ - QOpenGLContext *ctx = QOpenGLContext::currentContext(); - if (!ctx) - return false; - - QOpenGLExtraFunctions *f = ctx->extraFunctions(); - *program = f->glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &fragmentShaderSource); - GLint status = 0; - f->glGetProgramiv(*program, GL_LINK_STATUS, &status); - if (!status) { - GLint len = 0; - f->glGetProgramiv(*program, GL_INFO_LOG_LENGTH, &len); - if (len) { - QByteArray s; - s.resize(len); - f->glGetProgramInfoLog(*program, s.count(), nullptr, s.data()); - qWarning("Failed to create separable shader program:\n%s", s.constData()); - } - return false; - } - - f->glGenProgramPipelines(1, pipeline); - f->glUseProgramStages(*pipeline, GL_FRAGMENT_SHADER_BIT, *program); - f->glActiveShaderProgram(*pipeline, *program); - - f->glValidateProgramPipeline(*pipeline); - status = 0; - f->glGetProgramPipelineiv(*pipeline, GL_VALIDATE_STATUS, &status); - if (!status) { - GLint len = 0; - f->glGetProgramPipelineiv(*pipeline, GL_INFO_LOG_LENGTH, &len); - if (len) { - QByteArray s; - s.resize(len); - f->glGetProgramPipelineInfoLog(*pipeline, s.count(), nullptr, s.data()); - qWarning("Program pipeline validation failed:\n%s", s.constData()); - } - return false; - } - - return true; -} - -#define PROC(type, name) reinterpret_cast(ctx->getProcAddress(#name)) - -bool QQuickNvprFunctionsPrivate::resolve() -{ - QOpenGLContext *ctx = QOpenGLContext::currentContext(); - - q->genPaths = PROC(PFNGLGENPATHSNVPROC, glGenPathsNV); - q->deletePaths = PROC(PFNGLDELETEPATHSNVPROC, glDeletePathsNV); - q->isPath = PROC(PFNGLISPATHNVPROC, glIsPathNV); - q->pathCommands = PROC(PFNGLPATHCOMMANDSNVPROC, glPathCommandsNV); - q->pathCoords = PROC(PFNGLPATHCOORDSNVPROC, glPathCoordsNV); - q->pathSubCommands = PROC(PFNGLPATHSUBCOMMANDSNVPROC, glPathSubCommandsNV); - q->pathSubCoords = PROC(PFNGLPATHSUBCOORDSNVPROC, glPathSubCoordsNV); - q->pathString = PROC(PFNGLPATHSTRINGNVPROC, glPathStringNV); - q->pathGlyphs = PROC(PFNGLPATHGLYPHSNVPROC, glPathGlyphsNV); - q->pathGlyphRange = PROC(PFNGLPATHGLYPHRANGENVPROC, glPathGlyphRangeNV); - q->weightPaths = PROC(PFNGLWEIGHTPATHSNVPROC, glWeightPathsNV); - q->copyPath = PROC(PFNGLCOPYPATHNVPROC, glCopyPathNV); - q->interpolatePaths = PROC(PFNGLINTERPOLATEPATHSNVPROC, glInterpolatePathsNV); - q->transformPath = PROC(PFNGLTRANSFORMPATHNVPROC, glTransformPathNV); - q->pathParameteriv = PROC(PFNGLPATHPARAMETERIVNVPROC, glPathParameterivNV); - q->pathParameteri = PROC(PFNGLPATHPARAMETERINVPROC, glPathParameteriNV); - q->pathParameterfv = PROC(PFNGLPATHPARAMETERFVNVPROC, glPathParameterfvNV); - q->pathParameterf = PROC(PFNGLPATHPARAMETERFNVPROC, glPathParameterfNV); - q->pathDashArray = PROC(PFNGLPATHDASHARRAYNVPROC, glPathDashArrayNV); - q->pathStencilFunc = PROC(PFNGLPATHSTENCILFUNCNVPROC, glPathStencilFuncNV); - q->pathStencilDepthOffset = PROC(PFNGLPATHSTENCILDEPTHOFFSETNVPROC, glPathStencilDepthOffsetNV); - q->stencilFillPath = PROC(PFNGLSTENCILFILLPATHNVPROC, glStencilFillPathNV); - q->stencilStrokePath = PROC(PFNGLSTENCILSTROKEPATHNVPROC, glStencilStrokePathNV); - q->stencilFillPathInstanced = PROC(PFNGLSTENCILFILLPATHINSTANCEDNVPROC, glStencilFillPathInstancedNV); - q->stencilStrokePathInstanced = PROC(PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC, glStencilStrokePathInstancedNV); - q->pathCoverDepthFunc = PROC(PFNGLPATHCOVERDEPTHFUNCNVPROC, glPathCoverDepthFuncNV); - q->pathColorGen = PROC(PFNGLPATHCOLORGENNVPROC, glPathColorGenNV); - q->pathTexGen = PROC(PFNGLPATHTEXGENNVPROC, glPathTexGenNV); - q->pathFogGen = PROC(PFNGLPATHFOGGENNVPROC, glPathFogGenNV); - q->coverFillPath = PROC(PFNGLCOVERFILLPATHNVPROC, glCoverFillPathNV); - q->coverStrokePath = PROC(PFNGLCOVERSTROKEPATHNVPROC, glCoverStrokePathNV); - q->coverFillPathInstanced = PROC(PFNGLCOVERFILLPATHINSTANCEDNVPROC, glCoverFillPathInstancedNV); - q->coverStrokePathInstanced = PROC(PFNGLCOVERSTROKEPATHINSTANCEDNVPROC, glCoverStrokePathInstancedNV); - q->getPathParameteriv = PROC(PFNGLGETPATHPARAMETERIVNVPROC, glGetPathParameterivNV); - q->getPathParameterfv = PROC(PFNGLGETPATHPARAMETERFVNVPROC, glGetPathParameterfvNV); - q->getPathCommands = PROC(PFNGLGETPATHCOMMANDSNVPROC, glGetPathCommandsNV); - q->getPathCoords = PROC(PFNGLGETPATHCOORDSNVPROC, glGetPathCoordsNV); - q->getPathDashArray = PROC(PFNGLGETPATHDASHARRAYNVPROC, glGetPathDashArrayNV); - q->getPathMetrics = PROC(PFNGLGETPATHMETRICSNVPROC, glGetPathMetricsNV); - q->getPathMetricRange = PROC(PFNGLGETPATHMETRICRANGENVPROC, glGetPathMetricRangeNV); - q->getPathSpacing = PROC(PFNGLGETPATHSPACINGNVPROC, glGetPathSpacingNV); - q->getPathColorgeniv = PROC(PFNGLGETPATHCOLORGENIVNVPROC, glGetPathColorGenivNV); - q->getPathColorgenfv = PROC(PFNGLGETPATHCOLORGENFVNVPROC, glGetPathColorGenfvNV); - q->getPathTexGeniv = PROC(PFNGLGETPATHTEXGENIVNVPROC, glGetPathTexGenivNV); - q->getPathTexGenfv = PROC(PFNGLGETPATHTEXGENFVNVPROC, glGetPathTexGenfvNV); - q->isPointInFillPath = PROC(PFNGLISPOINTINFILLPATHNVPROC, glIsPointInFillPathNV); - q->isPointInStrokePath = PROC(PFNGLISPOINTINSTROKEPATHNVPROC, glIsPointInStrokePathNV); - q->getPathLength = PROC(PFNGLGETPATHLENGTHNVPROC, glGetPathLengthNV); - q->getPointAlongPath = PROC(PFNGLPOINTALONGPATHNVPROC, glPointAlongPathNV); - q->matrixLoad3x2f = PROC(PFNGLMATRIXLOAD3X2FNVPROC, glMatrixLoad3x2fNV); - q->matrixLoad3x3f = PROC(PFNGLMATRIXLOAD3X3FNVPROC, glMatrixLoad3x3fNV); - q->matrixLoadTranspose3x3f = PROC(PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC, glMatrixLoadTranspose3x3fNV); - q->matrixMult3x2f = PROC(PFNGLMATRIXMULT3X2FNVPROC, glMatrixMult3x2fNV); - q->matrixMult3x3f = PROC(PFNGLMATRIXMULT3X3FNVPROC, glMatrixMult3x3fNV); - q->matrixMultTranspose3x3f = PROC(PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC, glMatrixMultTranspose3x3fNV); - q->stencilThenCoverFillPath = PROC(PFNGLSTENCILTHENCOVERFILLPATHNVPROC, glStencilThenCoverFillPathNV); - q->stencilThenCoverStrokePath = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC, glStencilThenCoverStrokePathNV); - q->stencilThenCoverFillPathInstanced = PROC(PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC, glStencilThenCoverFillPathInstancedNV); - q->stencilThenCoverStrokePathInstanced = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC, glStencilThenCoverStrokePathInstancedNV); - q->pathGlyphIndexRange = PROC(PFNGLPATHGLYPHINDEXRANGENVPROC, glPathGlyphIndexRangeNV); - q->pathGlyphIndexArray = PROC(PFNGLPATHGLYPHINDEXARRAYNVPROC, glPathGlyphIndexArrayNV); - q->pathMemoryGlyphIndexArray = PROC(PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC, glPathMemoryGlyphIndexArrayNV); - q->programPathFragmentInputGen = PROC(PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC, glProgramPathFragmentInputGenNV); - q->getProgramResourcefv = PROC(PFNGLGETPROGRAMRESOURCEFVNVPROC, glGetProgramResourcefvNV); - - q->matrixLoadf = PROC(PFNGLMATRIXLOADFEXTPROC, glMatrixLoadfEXT); - q->matrixLoadIdentity = PROC(PFNGLMATRIXLOADIDENTITYEXTPROC, glMatrixLoadIdentityEXT); - - return q->genPaths != nullptr // base path rendering ext - && q->programPathFragmentInputGen != nullptr // updated path rendering ext - && q->matrixLoadf != nullptr // direct state access ext - && q->matrixLoadIdentity != nullptr; -} - -QT_END_NAMESPACE - -#endif // QT_NO_OPENGL diff --git a/src/quick/util/qquicknvprfunctions_p.h b/src/quick/util/qquicknvprfunctions_p.h deleted file mode 100644 index 7900388305..0000000000 --- a/src/quick/util/qquicknvprfunctions_p.h +++ /dev/null @@ -1,406 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKNVPRFUNCTIONS_P_H -#define QQUICKNVPRFUNCTIONS_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of a number of Qt sources files. This header file may change from -// version to version without notice, or even be removed. -// -// We mean it. -// - -#include -#include - -#ifndef QT_NO_OPENGL - -QT_BEGIN_NAMESPACE - -#ifndef GL_NV_path_rendering -#define GL_PATH_FORMAT_SVG_NV 0x9070 -#define GL_PATH_FORMAT_PS_NV 0x9071 -#define GL_STANDARD_FONT_NAME_NV 0x9072 -#define GL_SYSTEM_FONT_NAME_NV 0x9073 -#define GL_FILE_NAME_NV 0x9074 -#define GL_PATH_STROKE_WIDTH_NV 0x9075 -#define GL_PATH_END_CAPS_NV 0x9076 -#define GL_PATH_INITIAL_END_CAP_NV 0x9077 -#define GL_PATH_TERMINAL_END_CAP_NV 0x9078 -#define GL_PATH_JOIN_STYLE_NV 0x9079 -#define GL_PATH_MITER_LIMIT_NV 0x907A -#define GL_PATH_DASH_CAPS_NV 0x907B -#define GL_PATH_INITIAL_DASH_CAP_NV 0x907C -#define GL_PATH_TERMINAL_DASH_CAP_NV 0x907D -#define GL_PATH_DASH_OFFSET_NV 0x907E -#define GL_PATH_CLIENT_LENGTH_NV 0x907F -#define GL_PATH_FILL_MODE_NV 0x9080 -#define GL_PATH_FILL_MASK_NV 0x9081 -#define GL_PATH_FILL_COVER_MODE_NV 0x9082 -#define GL_PATH_STROKE_COVER_MODE_NV 0x9083 -#define GL_PATH_STROKE_MASK_NV 0x9084 -#define GL_COUNT_UP_NV 0x9088 -#define GL_COUNT_DOWN_NV 0x9089 -#define GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A -#define GL_CONVEX_HULL_NV 0x908B -#define GL_BOUNDING_BOX_NV 0x908D -#define GL_TRANSLATE_X_NV 0x908E -#define GL_TRANSLATE_Y_NV 0x908F -#define GL_TRANSLATE_2D_NV 0x9090 -#define GL_TRANSLATE_3D_NV 0x9091 -#define GL_AFFINE_2D_NV 0x9092 -#define GL_AFFINE_3D_NV 0x9094 -#define GL_TRANSPOSE_AFFINE_2D_NV 0x9096 -#define GL_TRANSPOSE_AFFINE_3D_NV 0x9098 -#define GL_UTF8_NV 0x909A -#define GL_UTF16_NV 0x909B -#define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C -#define GL_PATH_COMMAND_COUNT_NV 0x909D -#define GL_PATH_COORD_COUNT_NV 0x909E -#define GL_PATH_DASH_ARRAY_COUNT_NV 0x909F -#define GL_PATH_COMPUTED_LENGTH_NV 0x90A0 -#define GL_PATH_FILL_BOUNDING_BOX_NV 0x90A1 -#define GL_PATH_STROKE_BOUNDING_BOX_NV 0x90A2 -#define GL_SQUARE_NV 0x90A3 -#define GL_ROUND_NV 0x90A4 -#define GL_TRIANGULAR_NV 0x90A5 -#define GL_BEVEL_NV 0x90A6 -#define GL_MITER_REVERT_NV 0x90A7 -#define GL_MITER_TRUNCATE_NV 0x90A8 -#define GL_SKIP_MISSING_GLYPH_NV 0x90A9 -#define GL_USE_MISSING_GLYPH_NV 0x90AA -#define GL_PATH_ERROR_POSITION_NV 0x90AB -#define GL_PATH_FOG_GEN_MODE_NV 0x90AC -#define GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD -#define GL_ADJACENT_PAIRS_NV 0x90AE -#define GL_FIRST_TO_REST_NV 0x90AF -#define GL_PATH_GEN_MODE_NV 0x90B0 -#define GL_PATH_GEN_COEFF_NV 0x90B1 -#define GL_PATH_GEN_COLOR_FORMAT_NV 0x90B2 -#define GL_PATH_GEN_COMPONENTS_NV 0x90B3 -#define GL_PATH_STENCIL_FUNC_NV 0x90B7 -#define GL_PATH_STENCIL_REF_NV 0x90B8 -#define GL_PATH_STENCIL_VALUE_MASK_NV 0x90B9 -#define GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD -#define GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE -#define GL_PATH_COVER_DEPTH_FUNC_NV 0x90BF -#define GL_PATH_DASH_OFFSET_RESET_NV 0x90B4 -#define GL_MOVE_TO_RESETS_NV 0x90B5 -#define GL_MOVE_TO_CONTINUES_NV 0x90B6 -#define GL_CLOSE_PATH_NV 0x00 -#define GL_MOVE_TO_NV 0x02 -#define GL_RELATIVE_MOVE_TO_NV 0x03 -#define GL_LINE_TO_NV 0x04 -#define GL_RELATIVE_LINE_TO_NV 0x05 -#define GL_HORIZONTAL_LINE_TO_NV 0x06 -#define GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07 -#define GL_VERTICAL_LINE_TO_NV 0x08 -#define GL_RELATIVE_VERTICAL_LINE_TO_NV 0x09 -#define GL_QUADRATIC_CURVE_TO_NV 0x0A -#define GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B -#define GL_CUBIC_CURVE_TO_NV 0x0C -#define GL_RELATIVE_CUBIC_CURVE_TO_NV 0x0D -#define GL_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E -#define GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F -#define GL_SMOOTH_CUBIC_CURVE_TO_NV 0x10 -#define GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11 -#define GL_SMALL_CCW_ARC_TO_NV 0x12 -#define GL_RELATIVE_SMALL_CCW_ARC_TO_NV 0x13 -#define GL_SMALL_CW_ARC_TO_NV 0x14 -#define GL_RELATIVE_SMALL_CW_ARC_TO_NV 0x15 -#define GL_LARGE_CCW_ARC_TO_NV 0x16 -#define GL_RELATIVE_LARGE_CCW_ARC_TO_NV 0x17 -#define GL_LARGE_CW_ARC_TO_NV 0x18 -#define GL_RELATIVE_LARGE_CW_ARC_TO_NV 0x19 -#define GL_RESTART_PATH_NV 0xF0 -#define GL_DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2 -#define GL_DUP_LAST_CUBIC_CURVE_TO_NV 0xF4 -#define GL_RECT_NV 0xF6 -#define GL_CIRCULAR_CCW_ARC_TO_NV 0xF8 -#define GL_CIRCULAR_CW_ARC_TO_NV 0xFA -#define GL_CIRCULAR_TANGENT_ARC_TO_NV 0xFC -#define GL_ARC_TO_NV 0xFE -#define GL_RELATIVE_ARC_TO_NV 0xFF -#define GL_BOLD_BIT_NV 0x01 -#define GL_ITALIC_BIT_NV 0x02 -#define GL_GLYPH_WIDTH_BIT_NV 0x01 -#define GL_GLYPH_HEIGHT_BIT_NV 0x02 -#define GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04 -#define GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08 -#define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10 -#define GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20 -#define GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40 -#define GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80 -#define GL_GLYPH_HAS_KERNING_BIT_NV 0x100 -#define GL_FONT_X_MIN_BOUNDS_BIT_NV 0x00010000 -#define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000 -#define GL_FONT_X_MAX_BOUNDS_BIT_NV 0x00040000 -#define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000 -#define GL_FONT_UNITS_PER_EM_BIT_NV 0x00100000 -#define GL_FONT_ASCENDER_BIT_NV 0x00200000 -#define GL_FONT_DESCENDER_BIT_NV 0x00400000 -#define GL_FONT_HEIGHT_BIT_NV 0x00800000 -#define GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000 -#define GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000 -#define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000 -#define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000 -#define GL_FONT_HAS_KERNING_BIT_NV 0x10000000 -#define GL_PRIMARY_COLOR_NV 0x852C -#define GL_SECONDARY_COLOR_NV 0x852D -#define GL_ROUNDED_RECT_NV 0xE8 -#define GL_RELATIVE_ROUNDED_RECT_NV 0xE9 -#define GL_ROUNDED_RECT2_NV 0xEA -#define GL_RELATIVE_ROUNDED_RECT2_NV 0xEB -#define GL_ROUNDED_RECT4_NV 0xEC -#define GL_RELATIVE_ROUNDED_RECT4_NV 0xED -#define GL_ROUNDED_RECT8_NV 0xEE -#define GL_RELATIVE_ROUNDED_RECT8_NV 0xEF -#define GL_RELATIVE_RECT_NV 0xF7 -#define GL_FONT_GLYPHS_AVAILABLE_NV 0x9368 -#define GL_FONT_TARGET_UNAVAILABLE_NV 0x9369 -#define GL_FONT_UNAVAILABLE_NV 0x936A -#define GL_FONT_UNINTELLIGIBLE_NV 0x936B -#define GL_CONIC_CURVE_TO_NV 0x1A -#define GL_RELATIVE_CONIC_CURVE_TO_NV 0x1B -#define GL_FONT_NUM_GLYPH_INDICES_BIT_NV 0x20000000 -#define GL_STANDARD_FONT_FORMAT_NV 0x936C -#define GL_2_BYTES_NV 0x1407 -#define GL_3_BYTES_NV 0x1408 -#define GL_4_BYTES_NV 0x1409 -#define GL_EYE_LINEAR_NV 0x2400 -#define GL_OBJECT_LINEAR_NV 0x2401 -#define GL_CONSTANT_NV 0x8576 -#define GL_PATH_PROJECTION_NV 0x1701 -#define GL_PATH_MODELVIEW_NV 0x1700 -#define GL_PATH_MODELVIEW_STACK_DEPTH_NV 0x0BA3 -#define GL_PATH_MODELVIEW_MATRIX_NV 0x0BA6 -#define GL_PATH_MAX_MODELVIEW_STACK_DEPTH_NV 0x0D36 -#define GL_PATH_TRANSPOSE_MODELVIEW_MATRIX_NV 0x84E3 -#define GL_PATH_PROJECTION_STACK_DEPTH_NV 0x0BA4 -#define GL_PATH_PROJECTION_MATRIX_NV 0x0BA7 -#define GL_PATH_MAX_PROJECTION_STACK_DEPTH_NV 0x0D38 -#define GL_PATH_TRANSPOSE_PROJECTION_MATRIX_NV 0x84E4 -#define GL_FRAGMENT_INPUT_NV 0x936D - -typedef GLuint (QOPENGLF_APIENTRYP PFNGLGENPATHSNVPROC) (GLsizei range); -typedef void (QOPENGLF_APIENTRYP PFNGLDELETEPATHSNVPROC) (GLuint path, GLsizei range); -typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPATHNVPROC) (GLuint path); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOMMANDSNVPROC) (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOORDSNVPROC) (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHSUBCOMMANDSNVPROC) (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHSUBCOORDSNVPROC) (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTRINGNVPROC) (GLuint path, GLenum format, GLsizei length, const void *pathString); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHGLYPHSNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHGLYPHRANGENVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); -typedef void (QOPENGLF_APIENTRYP PFNGLWEIGHTPATHSNVPROC) (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); -typedef void (QOPENGLF_APIENTRYP PFNGLCOPYPATHNVPROC) (GLuint resultPath, GLuint srcPath); -typedef void (QOPENGLF_APIENTRYP PFNGLINTERPOLATEPATHSNVPROC) (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); -typedef void (QOPENGLF_APIENTRYP PFNGLTRANSFORMPATHNVPROC) (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, const GLint *value); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERINVPROC) (GLuint path, GLenum pname, GLint value); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, const GLfloat *value); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERFNVPROC) (GLuint path, GLenum pname, GLfloat value); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHDASHARRAYNVPROC) (GLuint path, GLsizei dashCount, const GLfloat *dashArray); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTENCILFUNCNVPROC) (GLenum func, GLint ref, GLuint mask); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTENCILDEPTHOFFSETNVPROC) (GLfloat factor, GLfloat units); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOVERDEPTHFUNCNVPROC) (GLenum func); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOLORGENNVPROC) (GLenum color, GLenum genMode, GLenum colorFormat, const GLfloat *coeffs); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHTEXGENNVPROC) (GLenum texCoordSet, GLenum genMode, GLint components, const GLfloat *coeffs); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHFOGGENNVPROC) (GLenum genMode); -typedef void (QOPENGLF_APIENTRYP PFNGLCOVERFILLPATHNVPROC) (GLuint path, GLenum coverMode); -typedef void (QOPENGLF_APIENTRYP PFNGLCOVERSTROKEPATHNVPROC) (GLuint path, GLenum coverMode); -typedef void (QOPENGLF_APIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); -typedef void (QOPENGLF_APIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, GLint *value); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, GLfloat *value); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOMMANDSNVPROC) (GLuint path, GLubyte *commands); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOORDSNVPROC) (GLuint path, GLfloat *coords); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHDASHARRAYNVPROC) (GLuint path, GLfloat *dashArray); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHMETRICSNVPROC) (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHMETRICRANGENVPROC) (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHSPACINGNVPROC) (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOLORGENIVNVPROC) (GLenum color, GLenum pname, GLint *value); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOLORGENFVNVPROC) (GLenum color, GLenum pname, GLfloat *value); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHTEXGENIVNVPROC) (GLenum texCoordSet, GLenum pname, GLint *value); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHTEXGENFVNVPROC) (GLenum texCoordSet, GLenum pname, GLfloat *value); -typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPOINTINFILLPATHNVPROC) (GLuint path, GLuint mask, GLfloat x, GLfloat y); -typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPOINTINSTROKEPATHNVPROC) (GLuint path, GLfloat x, GLfloat y); -typedef GLfloat (QOPENGLF_APIENTRYP PFNGLGETPATHLENGTHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments); -typedef GLboolean (QOPENGLF_APIENTRYP PFNGLPOINTALONGPATHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOAD3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOAD3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULT3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULT3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask, GLenum coverMode); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); -typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHGLYPHINDEXRANGENVPROC) (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint baseAndCount[2]); -typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); -typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); -typedef void (QOPENGLF_APIENTRYP PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC) (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPROGRAMRESOURCEFVNVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLfloat *params); -#endif - -#ifndef GL_FLAT -#define GL_FLAT 0x1D00 -#endif - -#ifndef GL_INVERT -#define GL_INVERT 0x150A -#endif - -#ifndef GL_EXT_direct_state_access -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADFEXTPROC) (GLenum mode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC) (GLenum mode); -#endif - -// When building on a system with GLES 2.0 or 3.0, we may still compile the NVPR -// code path even though it's never used. Keep it compiling by defining the -// necessary ES 3.1 separable program constants. -#ifndef GL_FRAGMENT_SHADER_BIT -#define GL_FRAGMENT_SHADER_BIT 0x00000002 -#endif -#ifndef GL_UNIFORM -#define GL_UNIFORM 0x92E1 -#endif - -class QQuickNvprFunctionsPrivate; - -class QQuickNvprFunctions -{ -public: - QQuickNvprFunctions(); - ~QQuickNvprFunctions(); - - static QSurfaceFormat format(); - static bool isSupported(); - - bool create(); - - bool createFragmentOnlyPipeline(const char *fragmentShaderSource, GLuint *pipeline, GLuint *program); - - PFNGLGENPATHSNVPROC genPaths = nullptr; - PFNGLDELETEPATHSNVPROC deletePaths = nullptr; - PFNGLISPATHNVPROC isPath = nullptr; - PFNGLPATHCOMMANDSNVPROC pathCommands = nullptr; - PFNGLPATHCOORDSNVPROC pathCoords = nullptr; - PFNGLPATHSUBCOMMANDSNVPROC pathSubCommands = nullptr; - PFNGLPATHSUBCOORDSNVPROC pathSubCoords = nullptr; - PFNGLPATHSTRINGNVPROC pathString = nullptr; - PFNGLPATHGLYPHSNVPROC pathGlyphs = nullptr; - PFNGLPATHGLYPHRANGENVPROC pathGlyphRange = nullptr; - PFNGLWEIGHTPATHSNVPROC weightPaths = nullptr; - PFNGLCOPYPATHNVPROC copyPath = nullptr; - PFNGLINTERPOLATEPATHSNVPROC interpolatePaths = nullptr; - PFNGLTRANSFORMPATHNVPROC transformPath = nullptr; - PFNGLPATHPARAMETERIVNVPROC pathParameteriv = nullptr; - PFNGLPATHPARAMETERINVPROC pathParameteri = nullptr; - PFNGLPATHPARAMETERFVNVPROC pathParameterfv = nullptr; - PFNGLPATHPARAMETERFNVPROC pathParameterf = nullptr; - PFNGLPATHDASHARRAYNVPROC pathDashArray = nullptr; - PFNGLPATHSTENCILFUNCNVPROC pathStencilFunc = nullptr; - PFNGLPATHSTENCILDEPTHOFFSETNVPROC pathStencilDepthOffset = nullptr; - PFNGLSTENCILFILLPATHNVPROC stencilFillPath = nullptr; - PFNGLSTENCILSTROKEPATHNVPROC stencilStrokePath = nullptr; - PFNGLSTENCILFILLPATHINSTANCEDNVPROC stencilFillPathInstanced = nullptr; - PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC stencilStrokePathInstanced = nullptr; - PFNGLPATHCOVERDEPTHFUNCNVPROC pathCoverDepthFunc = nullptr; - PFNGLPATHCOLORGENNVPROC pathColorGen = nullptr; - PFNGLPATHTEXGENNVPROC pathTexGen = nullptr; - PFNGLPATHFOGGENNVPROC pathFogGen = nullptr; - PFNGLCOVERFILLPATHNVPROC coverFillPath = nullptr; - PFNGLCOVERSTROKEPATHNVPROC coverStrokePath = nullptr; - PFNGLCOVERFILLPATHINSTANCEDNVPROC coverFillPathInstanced = nullptr; - PFNGLCOVERSTROKEPATHINSTANCEDNVPROC coverStrokePathInstanced = nullptr; - PFNGLGETPATHPARAMETERIVNVPROC getPathParameteriv = nullptr; - PFNGLGETPATHPARAMETERFVNVPROC getPathParameterfv = nullptr; - PFNGLGETPATHCOMMANDSNVPROC getPathCommands = nullptr; - PFNGLGETPATHCOORDSNVPROC getPathCoords = nullptr; - PFNGLGETPATHDASHARRAYNVPROC getPathDashArray = nullptr; - PFNGLGETPATHMETRICSNVPROC getPathMetrics = nullptr; - PFNGLGETPATHMETRICRANGENVPROC getPathMetricRange = nullptr; - PFNGLGETPATHSPACINGNVPROC getPathSpacing = nullptr; - PFNGLGETPATHCOLORGENIVNVPROC getPathColorgeniv = nullptr; - PFNGLGETPATHCOLORGENFVNVPROC getPathColorgenfv = nullptr; - PFNGLGETPATHTEXGENIVNVPROC getPathTexGeniv = nullptr; - PFNGLGETPATHTEXGENFVNVPROC getPathTexGenfv = nullptr; - PFNGLISPOINTINFILLPATHNVPROC isPointInFillPath = nullptr; - PFNGLISPOINTINSTROKEPATHNVPROC isPointInStrokePath = nullptr; - PFNGLGETPATHLENGTHNVPROC getPathLength = nullptr; - PFNGLPOINTALONGPATHNVPROC getPointAlongPath = nullptr; - PFNGLMATRIXLOAD3X2FNVPROC matrixLoad3x2f = nullptr; - PFNGLMATRIXLOAD3X3FNVPROC matrixLoad3x3f = nullptr; - PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC matrixLoadTranspose3x3f = nullptr; - PFNGLMATRIXMULT3X2FNVPROC matrixMult3x2f = nullptr; - PFNGLMATRIXMULT3X3FNVPROC matrixMult3x3f = nullptr; - PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC matrixMultTranspose3x3f = nullptr; - PFNGLSTENCILTHENCOVERFILLPATHNVPROC stencilThenCoverFillPath = nullptr; - PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC stencilThenCoverStrokePath = nullptr; - PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC stencilThenCoverFillPathInstanced = nullptr; - PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC stencilThenCoverStrokePathInstanced = nullptr; - PFNGLPATHGLYPHINDEXRANGENVPROC pathGlyphIndexRange = nullptr; - PFNGLPATHGLYPHINDEXARRAYNVPROC pathGlyphIndexArray = nullptr; - PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC pathMemoryGlyphIndexArray = nullptr; - PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC programPathFragmentInputGen = nullptr; - PFNGLGETPROGRAMRESOURCEFVNVPROC getProgramResourcefv = nullptr; - - PFNGLMATRIXLOADFEXTPROC matrixLoadf = nullptr; - PFNGLMATRIXLOADIDENTITYEXTPROC matrixLoadIdentity = nullptr; - -private: - QQuickNvprFunctionsPrivate *d; -}; - -QT_END_NAMESPACE - -#endif // QT_NO_OPENGL - -#endif // QQUICKNVPRFUNCTIONS_P_H diff --git a/src/quick/util/qquicknvprfunctions_p_p.h b/src/quick/util/qquicknvprfunctions_p_p.h deleted file mode 100644 index 6df20566af..0000000000 --- a/src/quick/util/qquicknvprfunctions_p_p.h +++ /dev/null @@ -1,70 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKNVPRFUNCTIONS_P_P_H -#define QQUICKNVPRFUNCTIONS_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of a number of Qt sources files. This header file may change from -// version to version without notice, or even be removed. -// -// We mean it. -// - -#include "qquicknvprfunctions_p.h" - -QT_BEGIN_NAMESPACE - -class QQuickNvprFunctionsPrivate -{ -public: - QQuickNvprFunctionsPrivate(QQuickNvprFunctions *q_ptr) : q(q_ptr) { } - - bool resolve(); - - QQuickNvprFunctions *q; -}; - -QT_END_NAMESPACE - -#endif // QQUICKNVPRFUNCTIONS_P_P_H diff --git a/src/quick/util/qquicksvgparser_p.h b/src/quick/util/qquicksvgparser_p.h index 44b0d1b6dd..1777b99bf4 100644 --- a/src/quick/util/qquicksvgparser_p.h +++ b/src/quick/util/qquicksvgparser_p.h @@ -51,6 +51,7 @@ // We mean it. // +#include #include #include @@ -59,9 +60,9 @@ QT_BEGIN_NAMESPACE namespace QQuickSvgParser { bool parsePathDataFast(const QString &dataStr, QPainterPath &path); - void pathArc(QPainterPath &path, qreal rx, qreal ry, qreal x_axis_rotation, - int large_arc_flag, int sweep_flag, qreal x, qreal y, qreal curx, - qreal cury); + Q_QUICK_PRIVATE_EXPORT void pathArc(QPainterPath &path, qreal rx, qreal ry, qreal x_axis_rotation, + int large_arc_flag, int sweep_flag, qreal x, qreal y, qreal curx, + qreal cury); } QT_END_NAMESPACE diff --git a/src/quick/util/util.pri b/src/quick/util/util.pri index 56eb8ea3b7..b53b132cce 100644 --- a/src/quick/util/util.pri +++ b/src/quick/util/util.pri @@ -79,11 +79,4 @@ qtConfig(quick-path) { $$PWD/qquickpath_p.h \ $$PWD/qquickpath_p_p.h \ $$PWD/qquickpathinterpolator_p.h - qtConfig(opengl) { - SOURCES += \ - $$PWD/qquicknvprfunctions.cpp - HEADERS += \ - $$PWD/qquicknvprfunctions_p.h \ - $$PWD/qquicknvprfunctions_p_p.h - } } -- cgit v1.2.3 From caa5358fe97becafeba9631f4c4d2fc69469afb1 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Wed, 26 Apr 2017 00:16:16 +0200 Subject: Fix concurrent loading of the same qmldir from different scripts QQmlQmldirData keeps a pointer to a QQmlScript::Import, and an integer priority. Each Blob that is waiting on it was setting its own import and priority even though the QQmlQmldirData itself was shared. This resulted in whichever one began loading last succeeding to load, and the rest failing. This change instead stores the import and priority data per-dependent Blob Fix was originally done by Josh Faust . I added the test. Task-number: QTBUG-30469 Change-Id: Id3d15569a999a7c22eeb12b431e5daf1ddae51dc Reviewed-by: Simon Hausmann --- src/qml/qml/qqmltypeloader.cpp | 41 ++++++++++++++++++++++++----------------- src/qml/qml/qqmltypeloader_p.h | 12 ++++++------ 2 files changed, 30 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 40bd2e5020..e4293596d8 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1320,8 +1320,8 @@ bool QQmlTypeLoader::Blob::fetchQmldir(const QUrl &url, const QV4::CompiledData: { QQmlQmldirData *data = typeLoader()->getQmldir(url); - data->setImport(import); - data->setPriority(priority); + data->setImport(this, import); + data->setPriority(this, priority); if (data->status() == Error) { // This qmldir must not exist - which is not an error @@ -1349,7 +1349,7 @@ bool QQmlTypeLoader::Blob::updateQmldir(QQmlQmldirData *data, const QV4::Compile QHash::iterator it = m_unresolvedImports.find(import); if (it != m_unresolvedImports.end()) { - *it = data->priority(); + *it = data->priority(this); } // Release this reference at destruction @@ -1482,7 +1482,7 @@ void QQmlTypeLoader::Blob::dependencyComplete(QQmlDataBlob *blob) if (blob->type() == QQmlDataBlob::QmldirFile) { QQmlQmldirData *data = static_cast(blob); - const QV4::CompiledData::Import *import = data->import(); + const QV4::CompiledData::Import *import = data->import(this); QList errors; if (!qmldirDataAvailable(data, &errors)) { @@ -1506,11 +1506,11 @@ bool QQmlTypeLoader::Blob::qmldirDataAvailable(QQmlQmldirData *data, QListimport(); - data->setImport(0); + const QV4::CompiledData::Import *import = data->import(this); + data->setImport(this, 0); - int priority = data->priority(); - data->setPriority(0); + int priority = data->priority(this); + data->setPriority(this, 0); if (import) { // Do we need to resolve this import? @@ -3069,7 +3069,7 @@ void QQmlScriptBlob::initializeFromCompilationUnit(QV4::CompiledData::Compilatio } QQmlQmldirData::QQmlQmldirData(const QUrl &url, QQmlTypeLoader *loader) -: QQmlTypeLoader::Blob(url, QmldirFile, loader), m_import(0), m_priority(0) +: QQmlTypeLoader::Blob(url, QmldirFile, loader) { } @@ -3078,24 +3078,31 @@ const QString &QQmlQmldirData::content() const return m_content; } -const QV4::CompiledData::Import *QQmlQmldirData::import() const +const QV4::CompiledData::Import *QQmlQmldirData::import(QQmlTypeLoader::Blob *blob) const { - return m_import; + QHash::const_iterator it = + m_imports.find(blob); + if (it == m_imports.end()) + return 0; + return *it; } -void QQmlQmldirData::setImport(const QV4::CompiledData::Import *import) +void QQmlQmldirData::setImport(QQmlTypeLoader::Blob *blob, const QV4::CompiledData::Import *import) { - m_import = import; + m_imports[blob] = import; } -int QQmlQmldirData::priority() const +int QQmlQmldirData::priority(QQmlTypeLoader::Blob *blob) const { - return m_priority; + QHash::const_iterator it = m_priorities.find(blob); + if (it == m_priorities.end()) + return 0; + return *it; } -void QQmlQmldirData::setPriority(int priority) +void QQmlQmldirData::setPriority(QQmlTypeLoader::Blob *blob, int priority) { - m_priority = priority; + m_priorities[blob] = priority; } void QQmlQmldirData::dataReceived(const SourceCodeData &data) diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 48e7d5cba4..f367fa6f58 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -572,11 +572,11 @@ private: public: const QString &content() const; - const QV4::CompiledData::Import *import() const; - void setImport(const QV4::CompiledData::Import *); + const QV4::CompiledData::Import *import(QQmlTypeLoader::Blob *) const; + void setImport(QQmlTypeLoader::Blob *, const QV4::CompiledData::Import *); - int priority() const; - void setPriority(int); + int priority(QQmlTypeLoader::Blob *) const; + void setPriority(QQmlTypeLoader::Blob *, int); protected: void dataReceived(const SourceCodeData &) override; @@ -584,8 +584,8 @@ protected: private: QString m_content; - const QV4::CompiledData::Import *m_import; - int m_priority; + QHash m_imports; + QHash m_priorities; }; -- cgit v1.2.3 From 4f3bb75d2271717d932eb63a1193494d44eafb4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCri=20Valdmann?= Date: Wed, 3 May 2017 11:30:27 +0200 Subject: Doc: fix 'adpatation' -> 'adaptation' Change-Id: Ia852d86d9b9585a1d3d9b07eb8b48361d61c5671 Reviewed-by: Leena Miettinen --- src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc | 2 +- src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc | 2 +- src/quick/doc/src/concepts/visualcanvas/topic.qdoc | 2 +- src/quick/items/qquickwindow.cpp | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc index 76f863d07f..bcfdf311af 100644 --- a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc +++ b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc @@ -73,7 +73,7 @@ adaptations. The default adaptation capable of providing the full Qt Quick 2 feature set is the OpenGL adaptation. All of the details of the OpenGL -adpatation can are available here: +adaptation can are available here: \l{qtquick-visualcanvas-scenegraph-renderer.html}{OpenGL Adaptation} \section1 Software Adaptation diff --git a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc index 2e41c85873..cb14c72b04 100644 --- a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc +++ b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc @@ -32,7 +32,7 @@ \section1 The Scene Graph in Qt Quick Qt Quick 2 makes use of a dedicated scene graph based and a series of -adpatations of which the default uses OpenGL ES 2.0 or OpenGL 2.0 for +adaptations of which the default uses OpenGL ES 2.0 or OpenGL 2.0 for its rendering. Using a scene graph for graphics rather than the traditional imperative painting systems (QPainter and similar), means the scene to be rendered can be retained between diff --git a/src/quick/doc/src/concepts/visualcanvas/topic.qdoc b/src/quick/doc/src/concepts/visualcanvas/topic.qdoc index 168c616d06..f6b4024f7a 100644 --- a/src/quick/doc/src/concepts/visualcanvas/topic.qdoc +++ b/src/quick/doc/src/concepts/visualcanvas/topic.qdoc @@ -57,7 +57,7 @@ See the documentation about the \l{qtquick-visualcanvas-visualparent.html} Modern computer systems and devices use graphics processing units or GPUs to render graphics. Qt Quick can leverage this graphics hardware by using graphics -APIs like OpenGL. The default graphics adpatation for Qt Quick requires OpenGL and +APIs like OpenGL. The default graphics adaptation for Qt Quick requires OpenGL and it is used to display applications developed with Qt Quick in QML. In particular, Qt Quick defines a scene graph which is then rendered. See the documentation about the \l{qtquick-visualcanvas-scenegraph.html}{Scene Graph} for in-depth information about diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 8e78586697..90bdfd41e2 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -3853,7 +3853,7 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image, CreateText initialized or OpenGL is not in use. \note This function only has an effect when using the default OpenGL scene graph - adpation. + adaptation. \sa sceneGraphInitialized(), QSGTexture */ @@ -3961,7 +3961,7 @@ void QQuickWindow::setDefaultAlphaBuffer(bool useAlpha) graph renderer. Clear these manually on demand. \note This function only has an effect when using the default OpenGL scene graph - adpation. + adaptation. \sa QQuickWindow::beforeRendering() */ -- cgit v1.2.3 From 30dbe57521c9b1f4cac74db8f5f15a3c466c20d0 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Wed, 15 Mar 2017 11:59:14 +0100 Subject: QQmlComponent: Fix heap buffer overflow with bogus input Change-Id: I8a725018a5aeb39df370f856cd77d887faa511e3 Reviewed-by: Simon Hausmann --- src/qml/parser/qqmljslexer.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/qml/parser/qqmljslexer.cpp b/src/qml/parser/qqmljslexer.cpp index 66f9eac126..53e67fde03 100644 --- a/src/qml/parser/qqmljslexer.cpp +++ b/src/qml/parser/qqmljslexer.cpp @@ -724,6 +724,11 @@ again: return multilineStringLiteral ? T_MULTILINE_STRING_LITERAL : T_STRING_LITERAL; } else if (_char == QLatin1Char('\\')) { scanChar(); + if (_codePtr > _endPtr) { + _errorCode = IllegalEscapeSequence; + _errorMessage = QCoreApplication::translate("QQmlParser", "End of file reached at escape sequence"); + return T_ERROR; + } QChar u; -- cgit v1.2.3 From 0da6de65468d3b8cc1ca7ee0af10f254ae3d4dd6 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 3 May 2017 16:58:44 +0200 Subject: Fix ARM64 code generation When generating instructions for pointer arithmetic, do use the 64-bit registers, otherwise for example when loading pointers we'll end up only loading the lower 32 bits. Task-number: QTBUG-60441 Change-Id: I2c7c82964029e383afcadabc078842690d2d637a Reviewed-by: Robin Burchell --- src/3rdparty/masm/assembler/MacroAssemblerARM64.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/3rdparty/masm/assembler/MacroAssemblerARM64.h b/src/3rdparty/masm/assembler/MacroAssemblerARM64.h index 11f1672e15..d5f4acb3ca 100644 --- a/src/3rdparty/masm/assembler/MacroAssemblerARM64.h +++ b/src/3rdparty/masm/assembler/MacroAssemblerARM64.h @@ -214,27 +214,27 @@ public: #if defined(V4_BOOTSTRAP) void loadPtr(ImplicitAddress address, RegisterID dest) { - load32(address, dest); + load64(address, dest); } void subPtr(TrustedImm32 imm, RegisterID dest) { - sub32(imm, dest); + sub64(imm, dest); } void addPtr(TrustedImm32 imm, RegisterID dest) { - add32(imm, dest); + add64(imm, dest); } void addPtr(TrustedImm32 imm, RegisterID src, RegisterID dest) { - add32(imm, src, dest); + add64(imm, src, dest); } void storePtr(RegisterID src, ImplicitAddress address) { - store32(src, address); + store64(src, address); } #endif -- cgit v1.2.3 From 566eea09a851c221c557fc5c386ba3d0613e3b05 Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Tue, 2 May 2017 23:17:51 +0200 Subject: QV4BooleanObject: Avoid GC'd allocations when calling toString() Use the global strings instead. Change-Id: Ia43045ca3f40e80d44956cf8e38511cfc4c8a8bb Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4booleanobject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/qml/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp index 601066110f..c8e9ebb2dd 100644 --- a/src/qml/jsruntime/qv4booleanobject.cpp +++ b/src/qml/jsruntime/qv4booleanobject.cpp @@ -85,7 +85,7 @@ void BooleanPrototype::method_toString(const BuiltinFunction *, Scope &scope, Ca result = thisObject->value(); } - scope.result = scope.engine->newString(QLatin1String(result ? "true" : "false")); + scope.result = result ? scope.engine->id_true() : scope.engine->id_false(); } void BooleanPrototype::method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData) -- cgit v1.2.3 From a69edf01cb0a2a06282dc7b89724ffa542f6546d Mon Sep 17 00:00:00 2001 From: Kimmo Ollila Date: Wed, 3 May 2017 12:45:45 +0300 Subject: Add qtConfig(dlopen) check before adding libdl Change-Id: Iad67b9719fe6336b8dfc28de2e88463c588a0849 Reviewed-by: Simon Hausmann --- src/qml/compiler/compiler.pri | 2 +- src/qml/compiler/qv4compileddata.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index 31ee917b39..60f548a2b0 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -41,7 +41,7 @@ SOURCES += \ unix: SOURCES += $$PWD/qv4compilationunitmapper_unix.cpp else: SOURCES += $$PWD/qv4compilationunitmapper_win.cpp -qtConfig(private_tests):unix: QMAKE_USE_PRIVATE += libdl +qtConfig(private_tests):qtConfig(dlopen): QMAKE_USE_PRIVATE += libdl } qmldevtools_build|qtConfig(qml-interpreter) { diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 71546cc22e..d59a72cde2 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -734,7 +734,7 @@ static QByteArray ownLibraryChecksum() if (checksumInitialized) return libraryChecksum; checksumInitialized = true; -#if defined(Q_OS_UNIX) && !defined(QT_NO_DYNAMIC_CAST) && !defined(Q_OS_INTEGRITY) +#if !defined(QT_NO_DYNAMIC_CAST) && QT_CONFIG(dlopen) Dl_info libInfo; if (dladdr(reinterpret_cast(&ownLibraryChecksum), &libInfo) != 0) { QFile library(QFile::decodeName(libInfo.dli_fname)); -- cgit v1.2.3 From 0b403a9e3f81b63855e7285f5f7ec6bb497beb2e Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 20 Apr 2017 10:08:11 +0200 Subject: Fix remaining QQWindow qCDebugs to show eventpoint IDs in hex MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I04122218499733856136f5a49b72707a0e8885e5 Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickwindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 90bdfd41e2..98901ab818 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -751,7 +751,7 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) if (grabber && touchMouseId != -1 && touchMouseDevice) { // update the touch item for mouse touch id to the new grabber - qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << touchMouseId << "->" << q->mouseGrabberItem(); + qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << touchMouseId << "->" << q->mouseGrabberItem(); auto point = touchMouseDevice->pointerEvent()->pointById(touchMouseId); if (point) point->setGrabber(grabber); @@ -2227,7 +2227,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) QQuickEventPoint *point = event->point(i); if (point->state() == QQuickEventPoint::Released) { int id = point->pointId(); - qCDebug(DBG_TOUCH_TARGET) << "TP" << id << "released"; + qCDebug(DBG_TOUCH_TARGET) << "TP" << hex << id << "released"; point->setGrabber(nullptr); if (id == touchMouseId) { touchMouseId = -1; -- cgit v1.2.3 From 66724f59910a87f924f0fd7d9954ced9413676b1 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 3 May 2017 14:30:05 -0700 Subject: Add missing break Commit 2a812493bc97983b85110f853d3dbe57b54667d8 added the VariantMap case but forgot to add the break before it (there wasn't a break because it fell through to default: break). This is a 6.5 year old issue, though it affected no one because setVariantMapProperty checks the destination's type again. Found by GCC 7: qqmllistmodel.cpp:1075:62: warning: this statement may fall through [-Wimplicit-fallthrough=] target->setVariantProperty(targetRole, v); ^ qqmllistmodel.cpp:1077:13: note: here case ListLayout::Role::VariantMap: ^~~~ Change-Id: Ica9894dc9b5e48278fd4fffd14bb35efd18a8a6e Reviewed-by: Simon Hausmann --- src/qml/types/qqmllistmodel.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index 2011fcc4d6..4d8f213284 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -1071,6 +1071,7 @@ void ListElement::sync(ListElement *src, ListLayout *srcLayout, ListElement *tar QVariant v = src->getProperty(srcRole, 0, 0); target->setVariantProperty(targetRole, v); } + break; case ListLayout::Role::VariantMap: { QVariantMap *map = src->getVariantMapProperty(srcRole); -- cgit v1.2.3 From 81785d3eb8768ba9f932dd7105eee61ce39a2a5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCri=20Valdmann?= Date: Thu, 4 May 2017 15:53:41 +0200 Subject: Doc: fix some typos in qsgnode.cpp Change-Id: I9ce8f7e9dffbf7fd5280841bbe46d07eaaf9e235 Reviewed-by: Leena Miettinen --- src/quick/scenegraph/coreapi/qsgnode.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/quick/scenegraph/coreapi/qsgnode.cpp b/src/quick/scenegraph/coreapi/qsgnode.cpp index 7ac3914023..e400928d4e 100644 --- a/src/quick/scenegraph/coreapi/qsgnode.cpp +++ b/src/quick/scenegraph/coreapi/qsgnode.cpp @@ -792,7 +792,7 @@ QSGBasicGeometryNode::~QSGBasicGeometryNode() If the node has the flag QSGNode::OwnsGeometry set, it will also delete the geometry object it is pointing to. This flag is not set by default. - If the geometry is changed whitout calling setGeometry() again, the user + If the geometry is changed without calling setGeometry() again, the user must also mark the geometry as dirty using QSGNode::markDirty(). \sa markDirty() @@ -845,7 +845,7 @@ void QSGBasicGeometryNode::setGeometry(QSGGeometry *geometry) The geometry node supports two types of materials, the opaqueMaterial and the normal material. The opaqueMaterial is used when the accumulated scene graph opacity at the - time of rendering is 1. The primary usecase is to special case opaque rendering + time of rendering is 1. The primary use case is to special case opaque rendering to avoid an extra operation in the fragment shader can have significant performance impact on embedded graphics chips. The opaque material is optional. @@ -887,7 +887,7 @@ QSGGeometryNode::QSGGeometryNode(QSGGeometryNodePrivate &dd) Deletes this geometry node. The flags QSGNode::OwnsMaterial, QSGNode::OwnsOpaqueMaterial and - QSGNode::OwnsGeometry decides weither the geometry node should also + QSGNode::OwnsGeometry decides whether the geometry node should also delete the materials and geometry. By default, these flags are disabled. */ @@ -961,7 +961,7 @@ void QSGGeometryNode::setRenderOrder(int order) Geometry nodes must have a material before they can be added to the scene graph. - If the material is changed whitout calling setMaterial() again, the user + If the material is changed without calling setMaterial() again, the user must also mark the material as dirty using QSGNode::markDirty(). */ @@ -991,7 +991,7 @@ void QSGGeometryNode::setMaterial(QSGMaterial *material) allowed to set QSGMaterial::Blending to true and draw transparent pixels. - If the material is changed whitout calling setOpaqueMaterial() + If the material is changed without calling setOpaqueMaterial() again, the user must also mark the opaque material as dirty using QSGNode::markDirty(). @@ -1371,7 +1371,7 @@ void QSGOpacityNode::setOpacity(qreal opacity) Returns this node's accumulated opacity. - This vaule is calculated during rendering and only stored + This value is calculated during rendering and only stored in the opacity node temporarily. \internal -- cgit v1.2.3 From 1af055c68544d86a1beb2dda1fb9be6eeffe0a7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCri=20Valdmann?= Date: Thu, 4 May 2017 15:55:23 +0200 Subject: Doc: fix some typos in qsgrenderer.cpp Change-Id: I140e4e35d7841813df6425d0e418aa52660ed03b Reviewed-by: Leena Miettinen --- src/quick/scenegraph/coreapi/qsgrenderer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/quick/scenegraph/coreapi/qsgrenderer.cpp b/src/quick/scenegraph/coreapi/qsgrenderer.cpp index e5d464930c..0458e2dead 100644 --- a/src/quick/scenegraph/coreapi/qsgrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgrenderer.cpp @@ -99,7 +99,7 @@ void QSGBindableFboId::bind() const #endif /*! \class QSGRenderer - \brief The renderer class is the abstract baseclass use for rendering the + \brief The renderer class is the abstract baseclass used for rendering the QML scene graph. The renderer is not tied to any particular surface. It expects a context to @@ -292,7 +292,7 @@ void QSGRenderer::preprocess() // We need to take a copy here, in case any of the preprocess calls deletes a node that // is in the preprocess list and thus, changes the m_nodes_to_preprocess behind our backs - // For the default case, when this does not happen, the cost is neglishible. + // For the default case, when this does not happen, the cost is negligible. QSet items = m_nodes_to_preprocess; for (QSet::const_iterator it = items.constBegin(); -- cgit v1.2.3 From 8696993c4c3a92f2099e6160d516007a543f0116 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 4 May 2017 16:21:27 +0200 Subject: Fix crash in pre-cross-compiled ARMv7 code when host was 64-bit When encoding negative offsets for relative jumps, we must stay within signed 32-bit range to correctly perform all the different thumb offset encodings correctly. Task-number: QTBUG-60441 Change-Id: I0a7243debbcbc4d557710dddbd39cb97bd702da4 Reviewed-by: Lars Knoll --- src/3rdparty/masm/assembler/ARMv7Assembler.h | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/3rdparty/masm/assembler/ARMv7Assembler.h b/src/3rdparty/masm/assembler/ARMv7Assembler.h index 615c72fc15..d57e5a7c78 100644 --- a/src/3rdparty/masm/assembler/ARMv7Assembler.h +++ b/src/3rdparty/masm/assembler/ARMv7Assembler.h @@ -2531,12 +2531,18 @@ private: return (instruction[0] == OP_NOP_T2a) && (instruction[1] == OP_NOP_T2b); } + static int32_t makeRelative(const void *target, const void *source) + { + intptr_t difference = reinterpret_cast(target) - reinterpret_cast(source); + return static_cast(difference); + } + static bool canBeJumpT1(const uint16_t* instruction, const void* target) { ASSERT(!(reinterpret_cast(instruction) & 1)); ASSERT(!(reinterpret_cast(target) & 1)); - intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + auto relative = makeRelative(target, instruction); // It does not appear to be documented in the ARM ARM (big surprise), but // for OP_B_T1 the branch displacement encoded in the instruction is 2 // less than the actual displacement. @@ -2549,7 +2555,7 @@ private: ASSERT(!(reinterpret_cast(instruction) & 1)); ASSERT(!(reinterpret_cast(target) & 1)); - intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + auto relative = makeRelative(target, instruction); // It does not appear to be documented in the ARM ARM (big surprise), but // for OP_B_T2 the branch displacement encoded in the instruction is 2 // less than the actual displacement. @@ -2562,7 +2568,7 @@ private: ASSERT(!(reinterpret_cast(instruction) & 1)); ASSERT(!(reinterpret_cast(target) & 1)); - intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + auto relative = makeRelative(target, instruction); return ((relative << 11) >> 11) == relative; } @@ -2571,7 +2577,7 @@ private: ASSERT(!(reinterpret_cast(instruction) & 1)); ASSERT(!(reinterpret_cast(target) & 1)); - intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + auto relative = makeRelative(target, instruction); return ((relative << 7) >> 7) == relative; } @@ -2582,7 +2588,7 @@ private: ASSERT(!(reinterpret_cast(target) & 1)); ASSERT(canBeJumpT1(instruction, target)); - intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + auto relative = makeRelative(target, instruction); // It does not appear to be documented in the ARM ARM (big surprise), but // for OP_B_T1 the branch displacement encoded in the instruction is 2 // less than the actual displacement. @@ -2600,7 +2606,7 @@ private: ASSERT(!(reinterpret_cast(target) & 1)); ASSERT(canBeJumpT2(instruction, target)); - intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + auto relative = makeRelative(target, instruction); // It does not appear to be documented in the ARM ARM (big surprise), but // for OP_B_T2 the branch displacement encoded in the instruction is 2 // less than the actual displacement. @@ -2618,7 +2624,7 @@ private: ASSERT(!(reinterpret_cast(target) & 1)); ASSERT(canBeJumpT3(instruction, target)); - intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + auto relative = makeRelative(target, instruction); // All branch offsets should be an even distance. ASSERT(!(relative & 1)); @@ -2633,7 +2639,7 @@ private: ASSERT(!(reinterpret_cast(target) & 1)); ASSERT(canBeJumpT4(instruction, target)); - intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + auto relative = makeRelative(target, instruction); // ARM encoding for the top two bits below the sign bit is 'peculiar'. if (relative >= 0) relative ^= 0xC00000; -- cgit v1.2.3 From f4c0ea2554253bf5055be61e8f94078227b07298 Mon Sep 17 00:00:00 2001 From: Andy Nichols Date: Thu, 4 May 2017 16:32:56 +0300 Subject: Software: Fix leaking of SGTextures Task-number: QTBUG-59865 Change-Id: I18911734b34e535c2c77d5a860bd776105617663 Reviewed-by: Laszlo Agocs --- src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp index 1fa5234377..384c124a02 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp @@ -74,6 +74,9 @@ QSGSoftwareImageNode::~QSGSoftwareImageNode() void QSGSoftwareImageNode::setTexture(QSGTexture *texture) { + if (m_owns) + delete m_texture; + m_texture = texture; markDirty(DirtyMaterial); m_cachedMirroredPixmapIsDirty = true; } -- cgit v1.2.3 From 7bf3f99ceca5818596c7dd03d6053ea40aa262b3 Mon Sep 17 00:00:00 2001 From: Arnaud Vrac Date: Thu, 27 Apr 2017 18:56:02 +0200 Subject: qquickpixmapcache: fix crash when loading images asynchronously Use copied data instead of data that might be destroyed. This was already fixed in most places in commit 22c39eda8ab316c. Change-Id: Ie31ebb2e53945dd66ce3d0114629c284407ff26c Reviewed-by: Albert Astals Cid Reviewed-by: Shawn Rutledge Reviewed-by: Robin Burchell --- src/quick/util/qquickpixmapcache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp index 402897ca7a..882482ad31 100644 --- a/src/quick/util/qquickpixmapcache.cpp +++ b/src/quick/util/qquickpixmapcache.cpp @@ -497,7 +497,7 @@ void QQuickPixmapReader::networkRequestDone(QNetworkReply *reply) QByteArray all = reply->readAll(); QBuffer buff(&all); buff.open(QIODevice::ReadOnly); - if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, job->requestSize, job->data->providerOptions)) + if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, job->requestSize, job->providerOptions)) error = QQuickPixmapReply::Decoding; } // send completion event to the QQuickPixmapReply -- cgit v1.2.3 From 7730affa0dd50960da90f30d41b694a9e9ddd52b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 5 May 2017 15:21:40 +0200 Subject: Prospective build fix for architectures where we don't support the JIT Always export the isel factory method for qmlcachegen, so that we can link. Task-number: QTBUG-60597 Change-Id: Ia348ee5dfe0892878e8fce6c8afd30bb8eb54a51 Reviewed-by: Dmitry Shachnev Reviewed-by: Robin Burchell --- src/qml/jit/qv4isel_masm.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index 4afcd1517f..ac72d2e8f5 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -1631,14 +1631,20 @@ QQmlRefPointer ISelFactory::createU return result; } +#endif // ENABLE(ASSEMBLER) + QT_BEGIN_NAMESPACE namespace QV4 { namespace JIT { +#if ENABLE(ASSEMBLER) template class Q_QML_EXPORT InstructionSelection<>; template class Q_QML_EXPORT ISelFactory<>; +#endif + #if defined(V4_BOOTSTRAP) Q_QML_EXPORT QV4::EvalISelFactory *createISelForArchitecture(const QString &architecture) { +#if ENABLE(ASSEMBLER) using ARMv7CrossAssembler = QV4::JIT::Assembler>; using ARM64CrossAssembler = QV4::JIT::Assembler>; @@ -1659,6 +1665,7 @@ Q_QML_EXPORT QV4::EvalISelFactory *createISelForArchitecture(const QString &arch #endif if (!hostArch.isEmpty() && architecture == hostArch) return new ISelFactory<>; +#endif // ENABLE(ASSEMBLER) return nullptr; } @@ -1667,4 +1674,3 @@ Q_QML_EXPORT QV4::EvalISelFactory *createISelForArchitecture(const QString &arch } } QT_END_NAMESPACE -#endif // ENABLE(ASSEMBLER) -- cgit v1.2.3 From 7286b296f97d3394f3c2a716116bd483c9874931 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 4 May 2017 16:17:05 +0200 Subject: Touch child mouse filtering: pass not grabbed points inside item bounds c2ca2cbf04071ffb3aee6af8d5ab9084dfa1c091 started to restrict delivery of items in childMouseEventFilter by checking that we wouldn't deliver completely random points outside the item that were not grabbed by child items. That is generally correct. It did no longer send along touch points that had any other state but pressed but were inside when they had no grabber. That part was wrong, points must be sent along if they are not grabbed and inside the item. Task-number: QTBUG-60368 Change-Id: Ida24f5d2310d3b71db79ae5f95da2c57dcd3f150 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index ac0505da82..448b63c347 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -861,8 +861,9 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i } parent = parent->parentItem(); } - bool filterRelevant = isFiltering && grabberIsChild; + // when filtering, send points that are grabbed by a child and points that are not grabbed but inside + bool filterRelevant = isFiltering && (grabberIsChild || (!p->grabber() && item->contains(item->mapFromScene(p->scenePos())))); if (!(isGrabber || isPressInside || filterRelevant)) continue; -- cgit v1.2.3 From 02830aae1f3e9aaf89ca37637d40313babbff7b8 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 16 Nov 2016 16:30:39 +0100 Subject: Don't crash: Connections with a signal on a nonexistent object MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTBUG-56551 Change-Id: Ide09f177d3f6a3e9902f8ea904b3e6e4b998bd39 Reviewed-by: Jan Arve Sæther --- src/qml/qml/qqmlpropertycache.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 88ce2fa1b9..d18159841c 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -970,8 +970,11 @@ int QQmlPropertyCache::originalClone(QObject *object, int index) QQmlData *data = QQmlData::get(object, false); if (data && data->propertyCache) { QQmlPropertyCache *cache = data->propertyCache; - while (cache->signal(index)->isCloned()) + QQmlPropertyData *sig = cache->signal(index); + while (sig && sig->isCloned()) { --index; + sig = cache->signal(index); + } } else { while (QMetaObjectPrivate::signal(object->metaObject(), index).attributes() & QMetaMethod::Cloned) --index; -- cgit v1.2.3 From 99594f518a5fb657b75f68bba73537c4e9208e46 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 3 May 2017 08:45:28 +0200 Subject: Re-add some inline property storage It turns out that not using any inline property storage comes at a relatively high price in terms of memory consumption, as we always need to also create a memberData for any object. This avoids the memberData creation in quite a few cases, as we use the additional padding we have up to the 32 byte boundary given by the memory manager to store some property data. This complicates property access somewhat. To avoid performance regressions because of this, add specialized QV4::Lookup functions that optimize for properties that are inline or in the memberData struct. Change seems to be performance neutral on v8-bench on x86_64, but reduces peak memory usage when running the benchmark by around 20%. Change-Id: I0127d31a2d6038aaa540c4c4a1156f45ca3b7464 Reviewed-by: Simon Hausmann Reviewed-by: Robin Burchell --- src/qml/jsruntime/qv4arraydata.cpp | 2 + src/qml/jsruntime/qv4internalclass.cpp | 33 ++++++++- src/qml/jsruntime/qv4lookup.cpp | 128 ++++++++++++++++++++++++++++----- src/qml/jsruntime/qv4lookup_p.h | 13 ++-- src/qml/jsruntime/qv4managed.cpp | 2 + src/qml/jsruntime/qv4managed_p.h | 3 + src/qml/jsruntime/qv4object.cpp | 26 +++++-- src/qml/jsruntime/qv4object_p.h | 21 +++++- src/qml/memory/qv4heap_p.h | 2 + src/qml/memory/qv4mm.cpp | 7 +- src/qml/memory/qv4mm_p.h | 6 +- 11 files changed, 206 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp index c29cedaa9b..6d1fb62e0a 100644 --- a/src/qml/jsruntime/qv4arraydata.cpp +++ b/src/qml/jsruntime/qv4arraydata.cpp @@ -49,6 +49,8 @@ using namespace QV4; QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON const QV4::VTable QV4::ArrayData::static_vtbl = { + 0, + 0, 0, QV4::ArrayData::IsExecutionContext, QV4::ArrayData::IsString, diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index bac71b4537..7e3fd7dc12 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -43,6 +43,7 @@ #include #include "qv4object_p.h" #include "qv4identifiertable_p.h" +#include "qv4value_p.h" QT_BEGIN_NAMESPACE @@ -128,22 +129,48 @@ InternalClass::InternalClass(const QV4::InternalClass &other) static void insertHoleIntoPropertyData(Object *object, int idx) { + int inlineSize = object->d()->vt->nInlineProperties; int icSize = object->internalClass()->size; - int from = idx; + int from = qMax(idx, inlineSize); int to = from + 1; - if (from < icSize) + if (from < icSize) { memmove(object->propertyData(to), object->propertyData(from), (icSize - from - 1) * sizeof(Value)); + } + if (from == idx) + return; + if (inlineSize < icSize) + *object->propertyData(inlineSize) = *object->propertyData(inlineSize - 1); + from = idx; + to = from + 1; + if (from < inlineSize - 1) { + memmove(object->propertyData(to), object->propertyData(from), + (inlineSize - from - 1) * sizeof(Value)); + } } static void removeFromPropertyData(Object *object, int idx, bool accessor = false) { + int inlineSize = object->d()->vt->nInlineProperties; int delta = (accessor ? 2 : 1); int oldSize = object->internalClass()->size + delta; int to = idx; int from = to + delta; - if (from < oldSize) + if (from < inlineSize) { + memmove(object->propertyData(to), object->d()->propertyData(from), (inlineSize - from)*sizeof(Value)); + to = inlineSize - delta; + from = inlineSize; + } + if (to < inlineSize && from < oldSize) { + Q_ASSERT(from >= inlineSize); + memcpy(object->propertyData(to), object->d()->propertyData(from), (inlineSize - to)*sizeof(Value)); + to = inlineSize; + from = inlineSize + delta; + } + if (from < oldSize) { + Q_ASSERT(to >= inlineSize && from > to); memmove(object->propertyData(to), object->d()->propertyData(from), (oldSize - to)*sizeof(Value)); + } } void InternalClass::changeMember(Object *object, String *string, PropertyAttributes data, uint *index) diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index 52ed449664..80a24cdde7 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -307,15 +307,18 @@ ReturnedValue Lookup::getterTwoClasses(Lookup *l, ExecutionEngine *engine, const { Lookup l1 = *l; - if (l1.getter == Lookup::getter0 || l1.getter == Lookup::getter1) { + if (l1.getter == Lookup::getter0MemberData || l1.getter == Lookup::getter0Inline || l1.getter == Lookup::getter1) { if (const Object *o = object.as()) { ReturnedValue v = o->getLookup(l); Lookup l2 = *l; - if (l->index != UINT_MAX && (l2.getter == Lookup::getter0 || l2.getter == Lookup::getter1)) { - // if we have a getter0, make sure it comes first - if (l2.getter == Lookup::getter0) - qSwap(l1, l2); + if (l2.index != UINT_MAX) { + if (l1.getter != Lookup::getter0Inline) { + if (l2.getter == Lookup::getter0Inline || + (l1.getter != Lookup::getter0MemberData && l2.getter == Lookup::getter0MemberData)) + // sort the better getter first + qSwap(l1, l2); + } l->classList[0] = l1.classList[0]; l->classList[1] = l1.classList[1]; @@ -324,8 +327,22 @@ ReturnedValue Lookup::getterTwoClasses(Lookup *l, ExecutionEngine *engine, const l->index = l1.index; l->index2 = l2.index; - if (l1.getter == Lookup::getter0) { - l->getter = (l2.getter == Lookup::getter0) ? Lookup::getter0getter0 : Lookup::getter0getter1; + if (l1.getter == Lookup::getter0Inline) { + if (l2.getter == Lookup::getter0Inline) + l->getter = Lookup::getter0Inlinegetter0Inline; + else if (l2.getter == Lookup::getter0MemberData) + l->getter = Lookup::getter0Inlinegetter0MemberData; + else if (l2.getter == Lookup::getter1) + l->getter = Lookup::getter0Inlinegetter1; + else + Q_UNREACHABLE(); + } else if (l1.getter == Lookup::getter0MemberData) { + if (l2.getter == Lookup::getter0MemberData) + l->getter = Lookup::getter0MemberDatagetter0MemberData; + else if (l2.getter == Lookup::getter1) + l->getter = Lookup::getter0MemberDatagetter1; + else + Q_UNREACHABLE(); } else { Q_ASSERT(l1.getter == Lookup::getter1 && l2.getter == Lookup::getter1); l->getter = Lookup::getter1getter1; @@ -349,14 +366,26 @@ ReturnedValue Lookup::getterFallback(Lookup *l, ExecutionEngine *engine, const V return o->get(name); } -ReturnedValue Lookup::getter0(Lookup *l, ExecutionEngine *engine, const Value &object) +ReturnedValue Lookup::getter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object) { // we can safely cast to a QV4::Object here. If object is actually a string, // the internal class won't match Heap::Object *o = static_cast(object.heapObject()); if (o) { if (l->classList[0] == o->internalClass) - return o->propertyData(l->index)->asReturnedValue(); + return o->memberData->data[l->index].asReturnedValue(); + } + return getterTwoClasses(l, engine, object); +} + +ReturnedValue Lookup::getter0Inline(Lookup *l, ExecutionEngine *engine, const Value &object) +{ + // we can safely cast to a QV4::Object here. If object is actually a string, + // the internal class won't match + Heap::Object *o = static_cast(object.heapObject()); + if (o) { + if (l->classList[0] == o->internalClass) + return o->inlinePropertyData(l->index)->asReturnedValue(); } return getterTwoClasses(l, engine, object); } @@ -392,29 +421,74 @@ ReturnedValue Lookup::getter2(Lookup *l, ExecutionEngine *engine, const Value &o return getterFallback(l, engine, object); } -ReturnedValue Lookup::getter0getter0(Lookup *l, ExecutionEngine *engine, const Value &object) +ReturnedValue Lookup::getter0Inlinegetter0Inline(Lookup *l, ExecutionEngine *engine, const Value &object) { // we can safely cast to a QV4::Object here. If object is actually a string, // the internal class won't match Heap::Object *o = static_cast(object.heapObject()); if (o) { if (l->classList[0] == o->internalClass) - return o->propertyData(l->index)->asReturnedValue(); + return o->inlinePropertyData(l->index)->asReturnedValue(); if (l->classList[2] == o->internalClass) - return o->propertyData(l->index2)->asReturnedValue(); + return o->inlinePropertyData(l->index2)->asReturnedValue(); } l->getter = getterFallback; return getterFallback(l, engine, object); } -ReturnedValue Lookup::getter0getter1(Lookup *l, ExecutionEngine *engine, const Value &object) +ReturnedValue Lookup::getter0Inlinegetter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object) { // we can safely cast to a QV4::Object here. If object is actually a string, // the internal class won't match Heap::Object *o = static_cast(object.heapObject()); if (o) { if (l->classList[0] == o->internalClass) - return o->propertyData(l->index)->asReturnedValue(); + return o->inlinePropertyData(l->index)->asReturnedValue(); + if (l->classList[2] == o->internalClass) + return o->memberData->data[l->index2].asReturnedValue(); + } + l->getter = getterFallback; + return getterFallback(l, engine, object); +} + +ReturnedValue Lookup::getter0MemberDatagetter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object) +{ + // we can safely cast to a QV4::Object here. If object is actually a string, + // the internal class won't match + Heap::Object *o = static_cast(object.heapObject()); + if (o) { + if (l->classList[0] == o->internalClass) + return o->memberData->data[l->index].asReturnedValue(); + if (l->classList[2] == o->internalClass) + return o->memberData->data[l->index2].asReturnedValue(); + } + l->getter = getterFallback; + return getterFallback(l, engine, object); +} + +ReturnedValue Lookup::getter0Inlinegetter1(Lookup *l, ExecutionEngine *engine, const Value &object) +{ + // we can safely cast to a QV4::Object here. If object is actually a string, + // the internal class won't match + Heap::Object *o = static_cast(object.heapObject()); + if (o) { + if (l->classList[0] == o->internalClass) + return o->inlinePropertyData(l->index)->asReturnedValue(); + if (l->classList[2] == o->internalClass && l->classList[3] == o->prototype->internalClass) + return o->prototype->propertyData(l->index2)->asReturnedValue(); + } + l->getter = getterFallback; + return getterFallback(l, engine, object); +} + +ReturnedValue Lookup::getter0MemberDatagetter1(Lookup *l, ExecutionEngine *engine, const Value &object) +{ + // we can safely cast to a QV4::Object here. If object is actually a string, + // the internal class won't match + Heap::Object *o = static_cast(object.heapObject()); + if (o) { + if (l->classList[0] == o->internalClass) + return o->memberData->data[l->index].asReturnedValue(); if (l->classList[2] == o->internalClass && l->classList[3] == o->prototype->internalClass) return o->prototype->propertyData(l->index2)->asReturnedValue(); } @@ -604,9 +678,15 @@ ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionEngine *engine) ReturnedValue v = l->lookup(o, &attrs); if (v != Primitive::emptyValue().asReturnedValue()) { if (attrs.isData()) { - if (l->level == 0) - l->globalGetter = globalGetter0; - else if (l->level == 1) + if (l->level == 0) { + uint nInline = o->d()->vt->nInlineProperties; + if (l->index < nInline) + l->globalGetter = globalGetter0Inline; + else { + l->index -= nInline; + l->globalGetter = globalGetter0MemberData; + } + } else if (l->level == 1) l->globalGetter = globalGetter1; else if (l->level == 2) l->globalGetter = globalGetter2; @@ -626,11 +706,21 @@ ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionEngine *engine) return engine->throwReferenceError(n); } -ReturnedValue Lookup::globalGetter0(Lookup *l, ExecutionEngine *engine) +ReturnedValue Lookup::globalGetter0Inline(Lookup *l, ExecutionEngine *engine) +{ + Object *o = engine->globalObject; + if (l->classList[0] == o->internalClass()) + return o->d()->inlinePropertyData(l->index)->asReturnedValue(); + + l->globalGetter = globalGetterGeneric; + return globalGetterGeneric(l, engine); +} + +ReturnedValue Lookup::globalGetter0MemberData(Lookup *l, ExecutionEngine *engine) { Object *o = engine->globalObject; if (l->classList[0] == o->internalClass()) - return o->propertyData(l->index)->asReturnedValue(); + return o->d()->memberData->data[l->index].asReturnedValue(); l->globalGetter = globalGetterGeneric; return globalGetterGeneric(l, engine); diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index 2ffb43cce9..5a67c203ce 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -102,11 +102,15 @@ struct Lookup { static ReturnedValue getterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterFallback(Lookup *l, ExecutionEngine *engine, const Value &object); - static ReturnedValue getter0(Lookup *l, ExecutionEngine *engine, const Value &object); + static ReturnedValue getter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object); + static ReturnedValue getter0Inline(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getter1(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getter2(Lookup *l, ExecutionEngine *engine, const Value &object); - static ReturnedValue getter0getter0(Lookup *l, ExecutionEngine *engine, const Value &object); - static ReturnedValue getter0getter1(Lookup *l, ExecutionEngine *engine, const Value &object); + static ReturnedValue getter0Inlinegetter0Inline(Lookup *l, ExecutionEngine *engine, const Value &object); + static ReturnedValue getter0Inlinegetter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object); + static ReturnedValue getter0MemberDatagetter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object); + static ReturnedValue getter0Inlinegetter1(Lookup *l, ExecutionEngine *engine, const Value &object); + static ReturnedValue getter0MemberDatagetter1(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getter1getter1(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterAccessor0(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterAccessor1(Lookup *l, ExecutionEngine *engine, const Value &object); @@ -120,7 +124,8 @@ struct Lookup { static ReturnedValue arrayLengthGetter(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue globalGetterGeneric(Lookup *l, ExecutionEngine *engine); - static ReturnedValue globalGetter0(Lookup *l, ExecutionEngine *engine); + static ReturnedValue globalGetter0Inline(Lookup *l, ExecutionEngine *engine); + static ReturnedValue globalGetter0MemberData(Lookup *l, ExecutionEngine *engine); static ReturnedValue globalGetter1(Lookup *l, ExecutionEngine *engine); static ReturnedValue globalGetter2(Lookup *l, ExecutionEngine *engine); static ReturnedValue globalGetterAccessor0(Lookup *l, ExecutionEngine *engine); diff --git a/src/qml/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp index 3a84a83b9c..200380eda0 100644 --- a/src/qml/jsruntime/qv4managed.cpp +++ b/src/qml/jsruntime/qv4managed.cpp @@ -46,6 +46,8 @@ using namespace QV4; const VTable Managed::static_vtbl = { + 0, + 0, 0, Managed::IsExecutionContext, Managed::IsString, diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h index 5c764e7ff0..7e674c6ec7 100644 --- a/src/qml/jsruntime/qv4managed_p.h +++ b/src/qml/jsruntime/qv4managed_p.h @@ -129,6 +129,9 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} #define DEFINE_MANAGED_VTABLE_INT(classname, parentVTable) \ { \ parentVTable, \ + (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \ + (sizeof(classname::Data) + QV4::Chunk::SlotSize - 1)/QV4::Chunk::SlotSize*QV4::Chunk::SlotSize/sizeof(QV4::Value) \ + - (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \ classname::IsExecutionContext, \ classname::IsString, \ classname::IsObject, \ diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 12157af728..9595158ce9 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -61,9 +61,13 @@ DEFINE_OBJECT_VTABLE(Object); void Object::setInternalClass(InternalClass *ic) { d()->internalClass = ic; + uint nInline = d()->vtable()->nInlineProperties; + if (ic->size <= nInline) + return; bool hasMD = d()->memberData != nullptr; - if ((!hasMD && ic->size) || (hasMD && d()->memberData->size < ic->size)) - d()->memberData = MemberData::allocate(ic->engine, ic->size, d()->memberData); + uint requiredSize = ic->size - nInline; + if (!hasMD || (hasMD && d()->memberData->size < requiredSize)) + d()->memberData = MemberData::allocate(ic->engine, requiredSize, d()->memberData); } void Object::getProperty(uint index, Property *p, PropertyAttributes *attrs) const @@ -261,6 +265,13 @@ void Object::markObjects(Heap::Base *that, ExecutionEngine *e) o->arrayData->mark(e); if (o->prototype) o->prototype->mark(e); + uint nInline = o->vtable()->nInlineProperties; + Value *v = reinterpret_cast(o) + o->vt->inlinePropertyOffset; + const Value *end = v + nInline; + while (v < end) { + v->mark(e); + ++v; + } } void Object::insertMember(String *s, const Property *p, PropertyAttributes attributes) @@ -495,8 +506,15 @@ ReturnedValue Object::getLookup(const Managed *m, Lookup *l) ReturnedValue v = l->lookup(o, &attrs); if (v != Primitive::emptyValue().asReturnedValue()) { if (attrs.isData()) { - if (l->level == 0) - l->getter = Lookup::getter0; + if (l->level == 0) { + uint nInline = o->d()->vt->nInlineProperties; + if (l->index < nInline) + l->getter = Lookup::getter0Inline; + else { + l->index -= nInline; + l->getter = Lookup::getter0MemberData; + } + } else if (l->level == 1) l->getter = Lookup::getter1; else if (l->level == 2) diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 6a543ae1a8..c1d5fd66b2 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -71,8 +71,25 @@ struct Object : Base { void init() { Base::init(); } void destroy() { Base::destroy(); } - const Value *propertyData(uint index) const { return memberData->data + index; } - Value *propertyData(uint index) { return memberData->data + index; } + const Value *inlinePropertyData(uint index) const { + Q_ASSERT(index < vt->nInlineProperties); + return reinterpret_cast(this) + vt->inlinePropertyOffset + index; + } + + const Value *propertyData(uint index) const { + uint nInline = vt->nInlineProperties; + if (index < nInline) + return reinterpret_cast(this) + vt->inlinePropertyOffset + index; + index -= nInline; + return memberData->data + index; + } + Value *propertyData(uint index) { + uint nInline = vt->nInlineProperties; + if (index < nInline) + return reinterpret_cast(this) + vt->inlinePropertyOffset + index; + index -= nInline; + return memberData->data + index; + } InternalClass *internalClass; Pointer prototype; diff --git a/src/qml/memory/qv4heap_p.h b/src/qml/memory/qv4heap_p.h index 173c0a3e20..cd0a6d9a81 100644 --- a/src/qml/memory/qv4heap_p.h +++ b/src/qml/memory/qv4heap_p.h @@ -72,6 +72,8 @@ namespace QV4 { struct VTable { const VTable * const parent; + uint inlinePropertyOffset : 16; + uint nInlineProperties : 16; uint isExecutionContext : 1; uint isString : 1; uint isObject : 1; diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 7aa8f91503..0a6dfb9170 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -757,12 +757,15 @@ Heap::Base *MemoryManager::allocData(std::size_t size) return *m; } -Heap::Object *MemoryManager::allocObjectWithMemberData(std::size_t size, uint nMembers) +Heap::Object *MemoryManager::allocObjectWithMemberData(const QV4::VTable *vtable, uint nMembers) { + uint size = (vtable->nInlineProperties + vtable->inlinePropertyOffset)*sizeof(Value); + Q_ASSERT(!(size % sizeof(HeapItem))); Heap::Object *o = static_cast(allocData(size)); // ### Could optimize this and allocate both in one go through the block allocator - if (nMembers) { + if (nMembers > vtable->nInlineProperties) { + nMembers -= vtable->nInlineProperties; std::size_t memberSize = align(sizeof(Heap::MemberData) + (nMembers - 1)*sizeof(Value)); // qDebug() << "allocating member data for" << o << nMembers << memberSize; Heap::Base *m; diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h index 00daf8a622..9b4c2cd8df 100644 --- a/src/qml/memory/qv4mm_p.h +++ b/src/qml/memory/qv4mm_p.h @@ -231,7 +231,7 @@ public: template typename ObjectType::Data *allocateObject(InternalClass *ic) { - Heap::Object *o = allocObjectWithMemberData(align(sizeof(typename ObjectType::Data)), ic->size); + Heap::Object *o = allocObjectWithMemberData(ObjectType::staticVTable(), ic->size); o->setVtable(ObjectType::staticVTable()); o->internalClass = ic; return static_cast(o); @@ -241,7 +241,7 @@ public: typename ObjectType::Data *allocateObject() { InternalClass *ic = ObjectType::defaultInternalClass(engine); - Heap::Object *o = allocObjectWithMemberData(align(sizeof(typename ObjectType::Data)), ic->size); + Heap::Object *o = allocObjectWithMemberData(ObjectType::staticVTable(), ic->size); o->setVtable(ObjectType::staticVTable()); Object *prototype = ObjectType::defaultPrototype(engine); o->internalClass = ic; @@ -433,7 +433,7 @@ protected: /// expects size to be aligned Heap::Base *allocString(std::size_t unmanagedSize); Heap::Base *allocData(std::size_t size); - Heap::Object *allocObjectWithMemberData(std::size_t size, uint nMembers); + Heap::Object *allocObjectWithMemberData(const QV4::VTable *vtable, uint nMembers); #ifdef DETAILED_MM_STATS void willAllocate(std::size_t size); -- cgit v1.2.3 From 799f4a526375701d2b68d2ca1c85648994468394 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 3 May 2017 15:11:55 +0200 Subject: Optimize other lookups Add some more optimized lookups for accessing properties stored inline or in the memberData. Change-Id: Id74901d1dd91fd60933bf164c2bf90fed86232e3 Reviewed-by: Simon Hausmann Reviewed-by: Robin Burchell --- src/qml/jsruntime/qv4lookup.cpp | 70 ++++++++++++++++++++++++++++------------- src/qml/jsruntime/qv4lookup_p.h | 6 ++-- src/qml/jsruntime/qv4object.cpp | 2 +- src/qml/jsruntime/qv4object_p.h | 4 +++ 4 files changed, 58 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index 80a24cdde7..0d467098fe 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -284,11 +284,17 @@ ReturnedValue Lookup::getterGeneric(Lookup *l, ExecutionEngine *engine, const Va ReturnedValue v = l->lookup(object, proto, &attrs); if (v != Primitive::emptyValue().asReturnedValue()) { l->type = object.type(); - l->proto = proto; + l->proto = proto->d(); if (attrs.isData()) { - if (l->level == 0) - l->getter = Lookup::primitiveGetter0; - else if (l->level == 1) + if (l->level == 0) { + uint nInline = l->proto->vt->nInlineProperties; + if (l->index < nInline) + l->getter = Lookup::primitiveGetter0Inline; + else { + l->index -= nInline; + l->getter = Lookup::primitiveGetter0MemberData; + } + } else if (l->level == 1) l->getter = Lookup::primitiveGetter1; return v; } else { @@ -588,12 +594,23 @@ ReturnedValue Lookup::getterAccessor2(Lookup *l, ExecutionEngine *engine, const return getterFallback(l, engine, object); } -ReturnedValue Lookup::primitiveGetter0(Lookup *l, ExecutionEngine *engine, const Value &object) +ReturnedValue Lookup::primitiveGetter0Inline(Lookup *l, ExecutionEngine *engine, const Value &object) { if (object.type() == l->type) { - Object *o = l->proto; - if (l->classList[0] == o->internalClass()) - return o->propertyData(l->index)->asReturnedValue(); + Heap::Object *o = l->proto; + if (l->classList[0] == o->internalClass) + return o->inlinePropertyData(l->index)->asReturnedValue(); + } + l->getter = getterGeneric; + return getterGeneric(l, engine, object); +} + +ReturnedValue Lookup::primitiveGetter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object) +{ + if (object.type() == l->type) { + Heap::Object *o = l->proto; + if (l->classList[0] == o->internalClass) + return o->memberData->data[l->index].asReturnedValue(); } l->getter = getterGeneric; return getterGeneric(l, engine, object); @@ -602,10 +619,10 @@ ReturnedValue Lookup::primitiveGetter0(Lookup *l, ExecutionEngine *engine, const ReturnedValue Lookup::primitiveGetter1(Lookup *l, ExecutionEngine *engine, const Value &object) { if (object.type() == l->type) { - Object *o = l->proto; - if (l->classList[0] == o->internalClass() && - l->classList[1] == o->prototype()->internalClass) - return o->prototype()->propertyData(l->index)->asReturnedValue(); + Heap::Object *o = l->proto; + if (l->classList[0] == o->internalClass && + l->classList[1] == o->prototype->internalClass) + return o->prototype->propertyData(l->index)->asReturnedValue(); } l->getter = getterGeneric; return getterGeneric(l, engine, object); @@ -614,9 +631,9 @@ ReturnedValue Lookup::primitiveGetter1(Lookup *l, ExecutionEngine *engine, const ReturnedValue Lookup::primitiveGetterAccessor0(Lookup *l, ExecutionEngine *engine, const Value &object) { if (object.type() == l->type) { - Object *o = l->proto; - if (l->classList[0] == o->internalClass()) { - Scope scope(o->engine()); + Heap::Object *o = l->proto; + if (l->classList[0] == o->internalClass) { + Scope scope(o->internalClass->engine); ScopedFunctionObject getter(scope, o->propertyData(l->index + Object::GetterOffset)); if (!getter) return Encode::undefined(); @@ -634,11 +651,11 @@ ReturnedValue Lookup::primitiveGetterAccessor0(Lookup *l, ExecutionEngine *engin ReturnedValue Lookup::primitiveGetterAccessor1(Lookup *l, ExecutionEngine *engine, const Value &object) { if (object.type() == l->type) { - Object *o = l->proto; - if (l->classList[0] == o->internalClass() && - l->classList[1] == o->prototype()->internalClass) { - Scope scope(o->engine()); - ScopedFunctionObject getter(scope, o->prototype()->propertyData(l->index + Object::GetterOffset)); + Heap::Object *o = l->proto; + if (l->classList[0] == o->internalClass && + l->classList[1] == o->prototype->internalClass) { + Scope scope(o->internalClass->engine); + ScopedFunctionObject getter(scope, o->prototype->propertyData(l->index + Object::GetterOffset)); if (!getter) return Encode::undefined(); @@ -836,7 +853,7 @@ void Lookup::setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, if (Object *o = object.as()) { o->setLookup(l, value); - if (l->setter == Lookup::setter0) { + if (l->setter == Lookup::setter0 || l->setter == Lookup::setter0Inline) { l->setter = setter0setter0; l->classList[1] = l1.classList[0]; l->index2 = l1.index; @@ -869,6 +886,17 @@ void Lookup::setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Va setterTwoClasses(l, engine, object, value); } +void Lookup::setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) +{ + Object *o = object.as(); + if (o && o->internalClass() == l->classList[0]) { + *o->d()->inlinePropertyData(l->index) = value; + return; + } + + setterTwoClasses(l, engine, object, value); +} + void Lookup::setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Object *o = object.as(); diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index 5a67c203ce..9e50235b73 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -79,7 +79,7 @@ struct Lookup { struct { void *dummy0; void *dummy1; - Object *proto; + Heap::Object *proto; unsigned type; }; }; @@ -116,7 +116,8 @@ struct Lookup { static ReturnedValue getterAccessor1(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterAccessor2(Lookup *l, ExecutionEngine *engine, const Value &object); - static ReturnedValue primitiveGetter0(Lookup *l, ExecutionEngine *engine, const Value &object); + static ReturnedValue primitiveGetter0Inline(Lookup *l, ExecutionEngine *engine, const Value &object); + static ReturnedValue primitiveGetter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue primitiveGetter1(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue primitiveGetterAccessor0(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue primitiveGetterAccessor1(Lookup *l, ExecutionEngine *engine, const Value &object); @@ -136,6 +137,7 @@ struct Lookup { static void setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static void setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static void setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static void setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static void setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static void setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static void setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 9595158ce9..3d8e8f3ddf 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -549,7 +549,7 @@ void Object::setLookup(Managed *m, Lookup *l, const Value &value) if (idx != UINT_MAX && o->internalClass()->propertyData[idx].isData() && o->internalClass()->propertyData[idx].isWritable()) { l->classList[0] = o->internalClass(); l->index = idx; - l->setter = Lookup::setter0; + l->setter = idx < o->d()->vt->nInlineProperties ? Lookup::setter0Inline : Lookup::setter0; *o->propertyData(idx) = value; return; } diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index c1d5fd66b2..45392c0486 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -75,6 +75,10 @@ struct Object : Base { Q_ASSERT(index < vt->nInlineProperties); return reinterpret_cast(this) + vt->inlinePropertyOffset + index; } + Value *inlinePropertyData(uint index) { + Q_ASSERT(index < vt->nInlineProperties); + return reinterpret_cast(this) + vt->inlinePropertyOffset + index; + } const Value *propertyData(uint index) const { uint nInline = vt->nInlineProperties; -- cgit v1.2.3 From 5346da3f11f52530ef3c14d4ff16124b86f922e1 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Fri, 5 May 2017 14:20:37 +0200 Subject: Remove the qqmldebugtrace benchmark It benchmarks QElapsedTimer and QDataStream. We should do this in qtbase if we need to do it. The test was not in the parent qml.pro, but it was mentioned in some outdated comments in the QML profiler plugins. Remove those comments, too. Change-Id: I0d1341c32f4a2e02a04a958f76be015fe8d927fb Reviewed-by: Erik Verbruggen Reviewed-by: Lars Knoll --- src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp | 2 -- src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp | 2 -- 2 files changed, 4 deletions(-) (limited to 'src') diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp index 90e817e2fc..510c745d4e 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp @@ -66,8 +66,6 @@ QQmlProfilerAdapter::QQmlProfilerAdapter(QQmlProfilerService *service, QQmlEngin } // convert to QByteArrays that can be sent to the debug client -// use of QDataStream can skew results -// (see tst_qqmldebugtrace::trace() benchmark) static void qQmlProfilerDataToByteArrays(const QQmlProfilerData &d, QQmlProfiler::LocationHash &locations, QList &messages, diff --git a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp index 0c9fc36463..35beb0ee0d 100644 --- a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp +++ b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp @@ -74,8 +74,6 @@ QQuickProfilerAdapter::~QQuickProfilerAdapter() } // convert to QByteArrays that can be sent to the debug client -// use of QDataStream can skew results -// (see tst_qqmldebugtrace::trace() benchmark) static void qQuickProfilerDataToByteArrays(const QQuickProfilerData &data, QList &messages) { -- cgit v1.2.3 From 802a5be531a9f2b1396f4ea4715c9a1dc593f778 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Wed, 10 May 2017 10:09:56 +0200 Subject: Fix hover delivery in case of touch release events This fixes tst_TouchMouse::hoverEnabled. It turns out that the problem is that QQuickWindowPrivate::flushFrameSynchronousEvents would deliver artificial hover events which (due to the nature of the function) would arrive without being synchronized with the test. This should not be a problem as such, but there was one bug: the hover event would also be sent in case of a touch release event. The definition of when to "pretend hover" is a bit shaky, but we should definitely not send hover events after touch releases. By clearing lastMousePosition instead of setting it to where the touch point is released we no longer receive bogus events. Task-number: QTBUG-55350 Change-Id: I4dea54740e37182f66c4a729d7b06a1c770c34a9 Reviewed-by: Robin Burchell Reviewed-by: Simon Hausmann --- src/quick/items/qquickwindow.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 98901ab818..bdb362e1ff 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1979,8 +1979,14 @@ bool QQuickWindowPrivate::compressTouchEvent(QTouchEvent *event) void QQuickWindowPrivate::handleTouchEvent(QTouchEvent *event) { translateTouchEvent(event); - if (event->touchPoints().size()) - lastMousePosition = event->touchPoints().at(0).pos(); + if (event->touchPoints().size()) { + auto point = event->touchPoints().at(0); + if (point.state() == Qt::TouchPointReleased) { + lastMousePosition = QPointF(); + } else { + lastMousePosition = point.pos(); + } + } qCDebug(DBG_TOUCH) << event; -- cgit v1.2.3 From e0c30279ec1fad88346ed3fb483bc3c672fdd01b Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Mon, 8 May 2017 11:25:10 +0200 Subject: Move pointerEvent instance to QQuickWindow With two or more windows, if events are being delivered to each, the grabbers can be different in each. We need unique instances of the QQuickPointerEvent objects for each window to avoid losing the grab state in the parent window while delivering a synthesized event to a subwindow, for example. Change-Id: I51da1212d573853969e32ad78f5b219d979a8a5c Task-number: QTBUG-57253 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents_p_p.h | 25 +++++------------- src/quick/items/qquickwindow.cpp | 52 ++++++++++++++++++++++++++++++-------- src/quick/items/qquickwindow_p.h | 4 +++ 3 files changed, 52 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index cf6f83e5b1..3735d68a85 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -332,9 +332,9 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPointerEvent : public QObject Q_PROPERTY(Qt::MouseButtons buttons READ buttons) public: - QQuickPointerEvent(QObject *parent = nullptr) + QQuickPointerEvent(QObject *parent = nullptr, QQuickPointerDevice *device = nullptr) : QObject(parent) - , m_device(nullptr) + , m_device(device) , m_event(nullptr) , m_button(Qt::NoButton) , m_pressedButtons(Qt::NoButton) @@ -385,8 +385,8 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPointerMouseEvent : public QQuickPointerEvent { Q_OBJECT public: - QQuickPointerMouseEvent(QObject *parent = nullptr) - : QQuickPointerEvent(parent), m_mousePoint(new QQuickEventPoint(this)) { } + QQuickPointerMouseEvent(QObject *parent = nullptr, QQuickPointerDevice *device = nullptr) + : QQuickPointerEvent(parent, device), m_mousePoint(new QQuickEventPoint(this)) { } QQuickPointerEvent *reset(QEvent *) override; bool isPressEvent() const override; @@ -411,8 +411,8 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPointerTouchEvent : public QQuickPointerEvent { Q_OBJECT public: - QQuickPointerTouchEvent(QObject *parent = nullptr) - : QQuickPointerEvent(parent) + QQuickPointerTouchEvent(QObject *parent = nullptr, QQuickPointerDevice *device = nullptr) + : QQuickPointerEvent(parent, device) , m_pointCount(0) , m_synthMouseEvent(QEvent::MouseMove, QPointF(), Qt::NoButton, Qt::NoButton, Qt::NoModifier) { } @@ -500,18 +500,10 @@ public: QQuickPointerDevice(DeviceType devType, PointerType pType, Capabilities caps, int maxPoints, int buttonCount, const QString &name, qint64 uniqueId = 0) : m_deviceType(devType), m_pointerType(pType), m_capabilities(caps) , m_maximumTouchPoints(maxPoints), m_buttonCount(buttonCount), m_name(name) - , m_uniqueId(QPointingDeviceUniqueId::fromNumericId(uniqueId)), m_event(nullptr) + , m_uniqueId(QPointingDeviceUniqueId::fromNumericId(uniqueId)) { - if (m_deviceType == Mouse) { - m_event = new QQuickPointerMouseEvent; - } else if (m_deviceType == TouchScreen || m_deviceType == TouchPad) { - m_event = new QQuickPointerTouchEvent; - } else { - Q_ASSERT(false); - } } - ~QQuickPointerDevice() { delete m_event; } DeviceType type() const { return m_deviceType; } PointerType pointerType() const { return m_pointerType; } Capabilities capabilities() const { return m_capabilities; } @@ -520,7 +512,6 @@ public: int buttonCount() const { return m_buttonCount; } QString name() const { return m_name; } QPointingDeviceUniqueId uniqueId() const { return m_uniqueId; } - QQuickPointerEvent *pointerEvent() const { return m_event; } static QQuickPointerDevice *touchDevice(QTouchDevice *d); static QList touchDevices(); @@ -535,8 +526,6 @@ private: int m_buttonCount; QString m_name; QPointingDeviceUniqueId m_uniqueId; - // the device-specific event instance which is reused during event delivery - QQuickPointerEvent *m_event; Q_DISABLE_COPY(QQuickPointerDevice) }; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index bdb362e1ff..c441cfc357 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -752,12 +752,12 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) if (grabber && touchMouseId != -1 && touchMouseDevice) { // update the touch item for mouse touch id to the new grabber qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << touchMouseId << "->" << q->mouseGrabberItem(); - auto point = touchMouseDevice->pointerEvent()->pointById(touchMouseId); + auto point = pointerEventInstance(touchMouseDevice)->pointById(touchMouseId); if (point) point->setGrabber(grabber); fromTouch = true; } else { - QQuickPointerEvent *event = QQuickPointerDevice::genericMouseDevice()->pointerEvent(); + QQuickPointerEvent *event = pointerEventInstance(QQuickPointerDevice::genericMouseDevice()); Q_ASSERT(event->pointCount() == 1); event->point(0)->setGrabber(grabber); } @@ -784,7 +784,7 @@ void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVectorpointerEvent()->pointById(id); + auto point = pointerEventInstance(touchMouseDevice)->pointById(id); auto touchMouseGrabber = point->grabber(); if (touchMouseGrabber) { point->setGrabber(nullptr); @@ -798,7 +798,7 @@ void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVectorpointerEvent()->pointById(id); + auto point = pointerEventInstance(device)->pointById(id); if (!point) continue; QQuickItem *oldGrabber = point->grabber(); @@ -824,7 +824,7 @@ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool to if (Q_LIKELY(touch)) { const auto touchDevices = QQuickPointerDevice::touchDevices(); for (auto device : touchDevices) { - auto pointerEvent = device->pointerEvent(); + auto pointerEvent = pointerEventInstance(device); for (int i = 0; i < pointerEvent->pointCount(); ++i) { if (pointerEvent->point(i)->grabber() == grabber) { pointerEvent->point(i)->setGrabber(nullptr); @@ -1493,13 +1493,13 @@ QQuickItem *QQuickWindow::mouseGrabberItem() const Q_D(const QQuickWindow); if (d->touchMouseId != -1 && d->touchMouseDevice) { - QQuickPointerEvent *event = d->touchMouseDevice->pointerEvent(); + QQuickPointerEvent *event = d->pointerEventInstance(d->touchMouseDevice); auto point = event->pointById(d->touchMouseId); Q_ASSERT(point); return point->grabber(); } - QQuickPointerEvent *event = QQuickPointerDevice::genericMouseDevice()->pointerEvent(); + QQuickPointerEvent *event = d->pointerEventInstance(QQuickPointerDevice::genericMouseDevice()); Q_ASSERT(event->pointCount()); return event->point(0)->grabber(); } @@ -1883,7 +1883,7 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event) // A TouchCancel event will typically not contain any points. // Deliver it to all items that have active touches. - QQuickPointerEvent *pointerEvent = QQuickPointerDevice::touchDevice(event->device())->pointerEvent(); + QQuickPointerEvent *pointerEvent = pointerEventInstance(QQuickPointerDevice::touchDevice(event->device())); QVector grabbers = pointerEvent->grabbers(); for (QQuickItem *grabber: qAsConst(grabbers)) { @@ -2113,6 +2113,32 @@ void QQuickWindowPrivate::flushFrameSynchronousEvents() } } +QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QQuickPointerDevice *device) const +{ + // the list of devices should be very small so a linear search should be ok + for (QQuickPointerEvent *e: pointerEventInstances) { + if (e->device() == device) + return e; + } + + QQuickPointerEvent *ev = nullptr; + QQuickWindow *q = const_cast(q_func()); + switch (device->type()) { + case QQuickPointerDevice::Mouse: + ev = new QQuickPointerMouseEvent(q, device); + break; + case QQuickPointerDevice::TouchPad: + case QQuickPointerDevice::TouchScreen: + ev = new QQuickPointerTouchEvent(q, device); + break; + default: + // TODO tablet event types + break; + } + pointerEventInstances << ev; + return ev; +} + /*! \internal Returns a QQuickPointerEvent instance suitable for wrapping and delivering \a event. @@ -2123,25 +2149,29 @@ void QQuickWindowPrivate::flushFrameSynchronousEvents() QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QEvent *event) const { QQuickPointerDevice *dev = nullptr; + QQuickPointerEvent *ev = nullptr; switch (event->type()) { case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: case QEvent::MouseMove: dev = QQuickPointerDevice::genericMouseDevice(); + ev = pointerEventInstance(dev); break; case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: case QEvent::TouchCancel: dev = QQuickPointerDevice::touchDevice(static_cast(event)->device()); + ev = pointerEventInstance(dev); break; // TODO tablet event types default: break; } - Q_ASSERT(dev); - return dev->pointerEvent()->reset(event); + + Q_ASSERT(ev); + return ev->reset(event); } void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) @@ -2616,7 +2646,7 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem if (touchMouseUnset) { // the point was grabbed as a pure touch point before, now it will be treated as mouse // but the old receiver still needs to be informed - if (auto oldGrabber = touchMouseDevice->pointerEvent()->pointById(tp.id())->grabber()) + if (auto oldGrabber = pointerEventInstance(touchMouseDevice)->pointById(tp.id())->grabber()) oldGrabber->touchUngrabEvent(); } touchMouseUnset = false; // We want to leave touchMouseId and touchMouseDevice set diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index be915903c6..b3ff5a2b35 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -162,6 +162,10 @@ public: void flushFrameSynchronousEvents(); void deliverDelayedTouchEvent(); + // the device-specific event instances which are reused during event delivery + mutable QVector pointerEventInstances; + QQuickPointerEvent *pointerEventInstance(QQuickPointerDevice *device) const; + // delivery of pointer events: QQuickPointerEvent *pointerEventInstance(QEvent *ev) const; void deliverPointerEvent(QQuickPointerEvent *); -- cgit v1.2.3 From 9921b48c83490b450241d6c172f1375ab4efb6b1 Mon Sep 17 00:00:00 2001 From: Michael Brasser Date: Tue, 9 May 2017 09:21:58 -0500 Subject: Ensure same glyph cache is used for same font at different sizes Change-Id: I46b62616fd8141f65786e9e7bcb1068bed460732 Task-number: QTBUG-60696 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/quick/scenegraph/util/qsgdistancefieldutil.cpp | 24 ++++++++++++++++++++-- src/quick/scenegraph/util/qsgdistancefieldutil_p.h | 4 +++- 2 files changed, 25 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/quick/scenegraph/util/qsgdistancefieldutil.cpp b/src/quick/scenegraph/util/qsgdistancefieldutil.cpp index 9ca9cdb107..84df7f3393 100644 --- a/src/quick/scenegraph/util/qsgdistancefieldutil.cpp +++ b/src/quick/scenegraph/util/qsgdistancefieldutil.cpp @@ -84,12 +84,32 @@ QSGDistanceFieldGlyphCacheManager::~QSGDistanceFieldGlyphCacheManager() QSGDistanceFieldGlyphCache *QSGDistanceFieldGlyphCacheManager::cache(const QRawFont &font) { - return m_caches.value(font, 0); + return m_caches.value(fontKey(font), 0); } void QSGDistanceFieldGlyphCacheManager::insertCache(const QRawFont &font, QSGDistanceFieldGlyphCache *cache) { - m_caches.insert(font, cache); + m_caches.insert(fontKey(font), cache); +} + +QString QSGDistanceFieldGlyphCacheManager::fontKey(const QRawFont &font) +{ + QFontEngine *fe = QRawFontPrivate::get(font)->fontEngine; + if (!fe->faceId().filename.isEmpty()) { + QByteArray keyName = fe->faceId().filename; + if (font.style() != QFont::StyleNormal) + keyName += QByteArray(" I"); + if (font.weight() != QFont::Normal) + keyName += ' ' + QByteArray::number(font.weight()); + keyName += QByteArray(" DF"); + return QString::fromUtf8(keyName); + } else { + return QString::fromLatin1("%1_%2_%3_%4") + .arg(font.familyName()) + .arg(font.styleName()) + .arg(font.weight()) + .arg(font.style()); + } } QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgdistancefieldutil_p.h b/src/quick/scenegraph/util/qsgdistancefieldutil_p.h index ad366cb4d4..354a48a81e 100644 --- a/src/quick/scenegraph/util/qsgdistancefieldutil_p.h +++ b/src/quick/scenegraph/util/qsgdistancefieldutil_p.h @@ -80,7 +80,9 @@ public: void setAntialiasingSpreadFunc(AntialiasingSpreadFunc func) { m_antialiasingSpread_func = func; } private: - QHash m_caches; + static QString fontKey(const QRawFont &font); + + QHash m_caches; ThresholdFunc m_threshold_func; AntialiasingSpreadFunc m_antialiasingSpread_func; -- cgit v1.2.3 From a225bddf67f4786c845193630d4ab20b99a2fc3a Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Fri, 28 Apr 2017 13:25:50 +0200 Subject: Update the alloca() support in QtQml qv4alloca_p.h dates from April 2013 and contained just the #includes, whereas the code in qtqmlglobal_p.h was introduced earlier this year in commit 87f016ea9eddc874d5cba7d79d0a487d5ef61761. This commit moves the macros to qv4alloca_p.h and centralizes the support there. This also updates the #include detection mechanism, by using QT_CONFIG(alloca_h) to determine which #include to use. See commit 98c1d516b7f7624f7fcd7b9046783e3903a6a42b in qtbase for more details. Task-number: QTBUG-59700 Started-by: Thiago Macieira Change-Id: Icd0e0d4b27cb4e5eb892fffd14b4b38005ce2ecb Reviewed-by: Thiago Macieira Reviewed-by: Lars Knoll --- src/qml/jsruntime/jsruntime.pri | 1 + src/qml/jsruntime/qv4alloca_p.h | 53 +++++++++++++++++++++++++++++++++++++---- src/qml/qtqmlglobal_p.h | 40 ------------------------------- 3 files changed, 49 insertions(+), 45 deletions(-) (limited to 'src') diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index 955cf585e4..76ac8d4a91 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -46,6 +46,7 @@ SOURCES += \ HEADERS += \ $$PWD/qv4global_p.h \ + $$PWD/qv4alloca_p.h \ $$PWD/qv4engine_p.h \ $$PWD/qv4context_p.h \ $$PWD/qv4math_p.h \ diff --git a/src/qml/jsruntime/qv4alloca_p.h b/src/qml/jsruntime/qv4alloca_p.h index 2f486988c1..c21878fa42 100644 --- a/src/qml/jsruntime/qv4alloca_p.h +++ b/src/qml/jsruntime/qv4alloca_p.h @@ -51,15 +51,58 @@ // We mean it. // -#include +#include -#if defined(Q_OS_WIN) +#if QT_CONFIG(alloca_h) +# include +#elif QT_CONFIG(alloca_malloc_h) # include -# ifndef __GNUC__ +// This does not matter unless compiling in strict standard mode. +# ifdef Q_OS_WIN # define alloca _alloca # endif -#elif !defined(Q_OS_BSD4) || defined(Q_OS_DARWIN) -# include +#else +# include +#endif + +// Define Q_ALLOCA_VAR macro to be used instead of #ifdeffing +// the occurrences of alloca() in case it's not supported. +// Q_ALLOCA_DECLARE and Q_ALLOCA_ASSIGN macros separate +// memory allocation from the declaration and RAII. +#define Q_ALLOCA_VAR(type, name, size) \ + Q_ALLOCA_DECLARE(type, name); \ + Q_ALLOCA_ASSIGN(type, name, size) + +#if QT_CONFIG(alloca) + +#define Q_ALLOCA_DECLARE(type, name) \ + type *name = 0 + +#define Q_ALLOCA_ASSIGN(type, name, size) \ + name = static_cast(alloca(size)) + +#else +QT_BEGIN_NAMESPACE +class Qt_AllocaWrapper +{ +public: + Qt_AllocaWrapper() { m_data = 0; } + ~Qt_AllocaWrapper() { free(m_data); } + void *data() { return m_data; } + void allocate(int size) { m_data = malloc(size); } +private: + void *m_data; +}; +QT_END_NAMESPACE + +#define Q_ALLOCA_DECLARE(type, name) \ + Qt_AllocaWrapper _qt_alloca_##name; \ + type *name = nullptr + +#define Q_ALLOCA_ASSIGN(type, name, size) \ + _qt_alloca_##name.allocate(size); \ + name = static_cast(_qt_alloca_##name.data()) + #endif #endif diff --git a/src/qml/qtqmlglobal_p.h b/src/qml/qtqmlglobal_p.h index 6547274d09..e9834ffc4c 100644 --- a/src/qml/qtqmlglobal_p.h +++ b/src/qml/qtqmlglobal_p.h @@ -55,46 +55,6 @@ #include #include -// Define Q_ALLOCA_VAR macro to be used instead of #ifdeffing -// the occurrences of alloca() in case it's not supported. -// Q_ALLOCA_DECLARE and Q_ALLOCA_ASSIGN macros separate -// memory allocation from the declaration and RAII. -#define Q_ALLOCA_VAR(type, name, size) \ - Q_ALLOCA_DECLARE(type, name); \ - Q_ALLOCA_ASSIGN(type, name, size) - -#if defined(QT_BOOTSTRAPPED) || QT_CONFIG(alloca) - -#define Q_ALLOCA_DECLARE(type, name) \ - type *name = 0 - -#define Q_ALLOCA_ASSIGN(type, name, size) \ - name = static_cast(alloca(size)) - -#else -QT_BEGIN_NAMESPACE -class Qt_AllocaWrapper -{ -public: - Qt_AllocaWrapper() { m_data = 0; } - ~Qt_AllocaWrapper() { free(m_data); } - void *data() { return m_data; } - void allocate(int size) { m_data = malloc(size); } -private: - void *m_data; -}; -QT_END_NAMESPACE - -#define Q_ALLOCA_DECLARE(type, name) \ - Qt_AllocaWrapper _qt_alloca_##name; \ - type *name = 0 - -#define Q_ALLOCA_ASSIGN(type, name, size) \ - _qt_alloca_##name.allocate(size); \ - name = static_cast(_qt_alloca_##name.data()) - -#endif - #define Q_QML_PRIVATE_EXPORT Q_QML_EXPORT #endif // QTQMLGLOBAL_P_H -- cgit v1.2.3 From 7434d05cc102f27d36b8efccb4e37f20a4886ea9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 9 May 2017 11:30:03 +0200 Subject: Fix build on MIPS32 Remove stack handling function that's dead code that doesn't compile. Task-number: QTBUG-58567 Change-Id: I704b0323522ce2a313d6cc85112f782872c3bf68 Reviewed-by: Lars Knoll --- src/3rdparty/masm/assembler/MacroAssembler.h | 8 -------- 1 file changed, 8 deletions(-) (limited to 'src') diff --git a/src/3rdparty/masm/assembler/MacroAssembler.h b/src/3rdparty/masm/assembler/MacroAssembler.h index f37861eb66..6e77a9ffb7 100644 --- a/src/3rdparty/masm/assembler/MacroAssembler.h +++ b/src/3rdparty/masm/assembler/MacroAssembler.h @@ -248,14 +248,6 @@ public: } #endif -#if CPU(MIPS) - void poke(FPRegisterID src, int index = 0) - { - ASSERT(!(index & 1)); - storeDouble(src, addressForPoke(index)); - } -#endif - // Backwards banches, these are currently all implemented using existing forwards branch mechanisms. void branchPtr(RelationalCondition cond, RegisterID op1, TrustedImmPtr imm, Label target) { -- cgit v1.2.3 From 095e7d378ee17838d9ba5d7a503da0608bb2fd0c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 9 May 2017 08:24:01 +0200 Subject: Optimize Runtime::method_get/setElement This is now actually just as fast as the lookup code, so disable the generation of lookups for indexed accesses for now. This saves some runtime memory, as we don't need the data structures for the lookup. We can reintroduce lookups, once they offer a real performance benefit. Change-Id: Idc3fa7b248e2e25b4b2cd60d5053e2815634c8b7 Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4isel_moth.cpp | 4 +- src/qml/jit/qv4isel_masm.cpp | 4 +- src/qml/jsruntime/qv4runtime.cpp | 104 +++++++++++++++++++++++++++++--------- 3 files changed, 83 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index aefb084971..dc6fe317e5 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -638,7 +638,7 @@ void InstructionSelection::getQObjectProperty(IR::Expr *base, int propertyIndex, void InstructionSelection::getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) { - if (useFastLookups) { + if (0 && useFastLookups) { Instruction::LoadElementLookup load; load.lookup = registerIndexedGetterLookup(); load.base = getParam(base); @@ -657,7 +657,7 @@ void InstructionSelection::getElement(IR::Expr *base, IR::Expr *index, IR::Expr void InstructionSelection::setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) { - if (useFastLookups) { + if (0 && useFastLookups) { Instruction::StoreElementLookup store; store.lookup = registerIndexedSetterLookup(); store.base = getParam(targetBase); diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index ac72d2e8f5..50d40f6f98 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -618,7 +618,7 @@ void InstructionSelection::setQObjectProperty(IR::Expr *source, IR template void InstructionSelection::getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) { - if (useFastLookups) { + if (0 && useFastLookups) { uint lookup = registerIndexedGetterLookup(); generateLookupCall(target, lookup, offsetof(QV4::Lookup, indexedGetter), PointerToValue(base), @@ -633,7 +633,7 @@ void InstructionSelection::getElement(IR::Expr *base, IR::Expr *in template void InstructionSelection::setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) { - if (useFastLookups) { + if (0 && useFastLookups) { uint lookup = registerIndexedSetterLookup(); generateLookupCall(JITAssembler::Void, lookup, offsetof(QV4::Lookup, indexedSetter), PointerToValue(targetBase), PointerToValue(targetIndex), diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 42bf24d7f3..4b440f5335 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -599,41 +599,53 @@ void Runtime::method_setProperty(ExecutionEngine *engine, const Value &object, i o->put(name, value); } -ReturnedValue Runtime::method_getElement(ExecutionEngine *engine, const Value &object, const Value &index) +static Q_NEVER_INLINE ReturnedValue getElementIntFallback(ExecutionEngine *engine, const Value &object, uint idx) { + Q_ASSERT(idx < UINT_MAX); Scope scope(engine); - uint idx = index.asArrayIndex(); ScopedObject o(scope, object); if (!o) { - if (idx < UINT_MAX) { - if (const String *str = object.as()) { - if (idx >= (uint)str->toQString().length()) { - return Encode::undefined(); - } - const QString s = str->toQString().mid(idx, 1); - return scope.engine->newString(s)->asReturnedValue(); + if (const String *str = object.as()) { + if (idx >= (uint)str->toQString().length()) { + return Encode::undefined(); } + const QString s = str->toQString().mid(idx, 1); + return scope.engine->newString(s)->asReturnedValue(); } if (object.isNullOrUndefined()) { - QString message = QStringLiteral("Cannot read property '%1' of %2").arg(index.toQStringNoThrow()).arg(object.toQStringNoThrow()); + QString message = QStringLiteral("Cannot read property '%1' of %2").arg(idx).arg(object.toQStringNoThrow()); return engine->throwTypeError(message); } o = RuntimeHelpers::convertToObject(scope.engine, object); - if (!o) // type error - return Encode::undefined(); + Q_ASSERT(!!o); // can't fail as null/undefined is covered above + } + + if (o->arrayData() && !o->arrayData()->attrs) { + ScopedValue v(scope, o->arrayData()->get(idx)); + if (!v->isEmpty()) + return v->asReturnedValue(); } - if (idx < UINT_MAX) { - if (o->arrayData() && !o->arrayData()->attrs) { - ScopedValue v(scope, o->arrayData()->get(idx)); - if (!v->isEmpty()) - return v->asReturnedValue(); + return o->getIndexed(idx); +} + +static Q_NEVER_INLINE ReturnedValue getElementFallback(ExecutionEngine *engine, const Value &object, const Value &index) +{ + Q_ASSERT(index.asArrayIndex() == UINT_MAX); + Scope scope(engine); + + ScopedObject o(scope, object); + if (!o) { + if (object.isNullOrUndefined()) { + QString message = QStringLiteral("Cannot read property '%1' of %2").arg(index.toQStringNoThrow()).arg(object.toQStringNoThrow()); + return engine->throwTypeError(message); } - return o->getIndexed(idx); + o = RuntimeHelpers::convertToObject(scope.engine, object); + Q_ASSERT(!!o); // can't fail as null/undefined is covered above } ScopedString name(scope, index.toString(engine)); @@ -642,18 +654,39 @@ ReturnedValue Runtime::method_getElement(ExecutionEngine *engine, const Value &o return o->get(name); } -void Runtime::method_setElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) +ReturnedValue Runtime::method_getElement(ExecutionEngine *engine, const Value &object, const Value &index) +{ + uint idx; + if (index.asArrayIndex(idx)) { + if (Heap::Base *b = object.heapObject()) { + if (b->vtable()->isObject) { + Heap::Object *o = static_cast(b); + if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) { + Heap::SimpleArrayData *s = o->arrayData.cast(); + if (idx < s->len) + if (!s->data(idx).isEmpty()) + return s->data(idx).asReturnedValue(); + } + } + } + return getElementIntFallback(engine, object, idx); + } + + return getElementFallback(engine, object, index); +} + +static Q_NEVER_INLINE void setElementFallback(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) { Scope scope(engine); ScopedObject o(scope, object.toObject(engine)); - if (scope.engine->hasException) + if (engine->hasException) return; - uint idx = index.asArrayIndex(); - if (idx < UINT_MAX) { - if (o->arrayType() == Heap::ArrayData::Simple) { - Heap::SimpleArrayData *s = static_cast(o->arrayData()); - if (s && idx < s->len && !s->data(idx).isEmpty()) { + uint idx; + if (index.asArrayIndex(idx)) { + if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple) { + Heap::SimpleArrayData *s = o->d()->arrayData.cast(); + if (idx < s->len) { s->data(idx) = value; return; } @@ -666,6 +699,27 @@ void Runtime::method_setElement(ExecutionEngine *engine, const Value &object, co o->put(name, value); } +void Runtime::method_setElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) +{ + uint idx; + if (index.asArrayIndex(idx)) { + if (Heap::Base *b = object.heapObject()) { + if (b->vtable()->isObject) { + Heap::Object *o = static_cast(b); + if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) { + Heap::SimpleArrayData *s = o->arrayData.cast(); + if (idx < s->len) { + s->data(idx) = value; + return; + } + } + } + } + } + + return setElementFallback(engine, object, index, value); +} + ReturnedValue Runtime::method_foreachIterator(ExecutionEngine *engine, const Value &in) { Scope scope(engine); -- cgit v1.2.3 From 59a70377683445f4355b5e3eef50869b61331cfa Mon Sep 17 00:00:00 2001 From: Berthold Krevert Date: Mon, 1 May 2017 23:03:41 +0200 Subject: Fix QSG_VISUALIZE=batches Since I362e1cb8e10 the batch renderer defaults to QSG_SEPARATE_INDEX_BUFFER which broke the batches visualization Change-Id: If1d51cabb0cc4a3a98ac2c01bd78789d08fe72f7 Reviewed-by: Gunnar Sletta Reviewed-by: Robin Burchell --- src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index b8ebeaca63..78f2c86f6c 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -2966,7 +2966,11 @@ void Renderer::visualizeBatch(Batch *b) for (int ds=0; dsdrawSets.size(); ++ds) { const DrawSet &set = b->drawSets.at(ds); glVertexAttribPointer(a.position, 2, a.type, false, g->sizeOfVertex(), (void *) (qintptr) (set.vertices)); +#ifdef QSG_SEPARATE_INDEX_BUFFER + glDrawElements(g->drawingMode(), set.indexCount, GL_UNSIGNED_SHORT, (void *) (qintptr) (b->ibo.data + set.indices)); +#else glDrawElements(g->drawingMode(), set.indexCount, GL_UNSIGNED_SHORT, (void *) (qintptr) (b->vbo.data + set.indices)); +#endif } } else { Element *e = b->first; -- cgit v1.2.3 From a9339e3ed4d2db11e75ed6c77fbe420708b44712 Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Sat, 22 Apr 2017 19:22:48 +0200 Subject: TestCase: Grow some new functionality on grabImage Add some meta-information about the image that we can use to do slightly stronger checks than just blindly poking pixel data (width & height props). In addition, when a test of a grabbed image fails (which they certainly do), it is always nice if we can perform some diagnostics. To this end, add a save() method to save the grabbed QImage to disk. Tests for the new functionality are blacklisted on Linux at present, as they do not yet appear stable, inheriting the other tst_grabImage flakiness. [ChangeLog][QmlTest] The returned object from TestCase::grabImage now has 'width', 'height', and 'size' properties for additional verification. In addition, there is a save() method to be able to persist the grabbed image to disk (for diagnostics purposes, for example). Change-Id: Ic651e0257102c514c39c3f648c05870f9d9c52e8 Reviewed-by: Mitch Curtis --- src/imports/testlib/TestCase.qml | 24 +++++++++++++++++++++++- src/qmltest/quicktestresult.cpp | 40 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/imports/testlib/TestCase.qml b/src/imports/testlib/TestCase.qml index 8c1744a2b2..dbed896c59 100644 --- a/src/imports/testlib/TestCase.qml +++ b/src/imports/testlib/TestCase.qml @@ -837,7 +837,14 @@ Item { Returns a snapshot image object of the given \a item. - The returned image object has the following methods: + The returned image object has the following properties: + \list + \li width Returns the width of the underlying image (since 5.10) + \li height Returns the height of the underlying image (since 5.10) + \li size Returns the size of the underlying image (since 5.10) + \endlist + + Additionally, the returned image object has the following methods: \list \li red(x, y) Returns the red channel value of the pixel at \a x, \a y position \li green(x, y) Returns the green channel value of the pixel at \a x, \a y position @@ -858,6 +865,21 @@ Item { var newImage = grabImage(rect); verify(!newImage.equals(image)); \endcode + \li save(path) Saves the image to the given \a path. If the image cannot + be saved, an exception will be thrown. (since 5.10) + + This can be useful to perform postmortem analysis on failing tests, for + example: + + \code + var image = grabImage(rect); + try { + compare(image.width, 100); + } catch (ex) { + image.save("debug.png"); + throw ex; + } + \endcode \endlist diff --git a/src/qmltest/quicktestresult.cpp b/src/qmltest/quicktestresult.cpp index dc6caf505b..3650df8f3a 100644 --- a/src/qmltest/quicktestresult.cpp +++ b/src/qmltest/quicktestresult.cpp @@ -1,5 +1,6 @@ /**************************************************************************** ** +** Copyright (C) 2017 Crimson AS ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** @@ -61,7 +62,10 @@ #include #include #include +#include #include +#include +#include #include #include @@ -77,6 +81,11 @@ extern bool qWaitForSignal(QObject *obj, const char* signal, int timeout = 5000) class Q_QUICK_TEST_EXPORT QuickTestImageObject : public QObject { Q_OBJECT + + Q_PROPERTY(int width READ width CONSTANT) + Q_PROPERTY(int height READ height CONSTANT) + Q_PROPERTY(QSize size READ size CONSTANT) + public: QuickTestImageObject(const QImage& img, QObject *parent = 0) : QObject(parent) @@ -127,6 +136,33 @@ public Q_SLOTS: return m_image == other->m_image; } + + void save(const QString &filePath) + { + QImageWriter writer(filePath); + if (!writer.write(m_image)) { + QQmlEngine *engine = qmlContext(this)->engine(); + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine->handle()); + v4->throwError(QStringLiteral("Can't save to %1: %2").arg(filePath, writer.errorString())); + } + } + +public: + int width() const + { + return m_image.width(); + } + + int height() const + { + return m_image.height(); + } + + QSize size() const + { + return m_image.size(); + } + private: QImage m_image; }; @@ -706,7 +742,9 @@ QObject *QuickTestResult::grabImage(QQuickItem *item) QImage grabbed = window->grabWindow(); QRectF rf(item->x(), item->y(), item->width(), item->height()); rf = rf.intersected(QRectF(0, 0, grabbed.width(), grabbed.height())); - return new QuickTestImageObject(grabbed.copy(rf.toAlignedRect())); + QObject *o = new QuickTestImageObject(grabbed.copy(rf.toAlignedRect())); + QQmlEngine::setContextForObject(o, qmlContext(this)); + return o; } return 0; } -- cgit v1.2.3 From 6336ac22caf393932ab404d078d670f17031689f Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Sat, 29 Apr 2017 23:40:01 +0200 Subject: QSGContext: Remove some dead code Unused since 906d5c5c40183468f9521277c6244a6c46730de6 (2013! :-)) Change-Id: Ie9e2326948279a46ddc933881a66847de66d51a9 Reviewed-by: Gunnar Sletta --- src/quick/scenegraph/qsgcontext.cpp | 8 -------- 1 file changed, 8 deletions(-) (limited to 'src') diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp index 2be84f4aec..d460794573 100644 --- a/src/quick/scenegraph/qsgcontext.cpp +++ b/src/quick/scenegraph/qsgcontext.cpp @@ -228,14 +228,6 @@ public: int m_good; }; -class QSGTextureCleanupEvent : public QEvent -{ -public: - QSGTextureCleanupEvent(QSGTexture *t) : QEvent(QEvent::User), texture(t) { } - ~QSGTextureCleanupEvent() { delete texture; } - QSGTexture *texture; -}; - /*! \class QSGContext -- cgit v1.2.3 From ee6b07b3ce8ba80632868181d45d96253acb1064 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 11 May 2017 20:25:47 +0200 Subject: Temporarily restore QQuickPointerDevice::pointerEvent() accessor This can be reverted as soon as the relevant qtlocation change is integrated. Task-number: QTBUG-57253 Change-Id: I72b71f61ba8fe421ac57c963801176098fe9f11c Reviewed-by: Robin Burchell --- src/quick/items/qquickevents_p_p.h | 7 +++++++ src/quick/items/qquickwindow.cpp | 5 ++++- 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 3735d68a85..323ecfa4ff 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -501,6 +501,7 @@ public: : m_deviceType(devType), m_pointerType(pType), m_capabilities(caps) , m_maximumTouchPoints(maxPoints), m_buttonCount(buttonCount), m_name(name) , m_uniqueId(QPointingDeviceUniqueId::fromNumericId(uniqueId)) + , m_event(nullptr) { } @@ -513,6 +514,8 @@ public: QString name() const { return m_name; } QPointingDeviceUniqueId uniqueId() const { return m_uniqueId; } + QQuickPointerEvent *pointerEvent() const { return m_event; } // deprecated + static QQuickPointerDevice *touchDevice(QTouchDevice *d); static QList touchDevices(); static QQuickPointerDevice *genericMouseDevice(); @@ -527,6 +530,10 @@ private: QString m_name; QPointingDeviceUniqueId m_uniqueId; + // the event instance used last time within the context of one window + QQuickPointerEvent *m_event; // deprecated + friend class QQuickWindowPrivate; // not needed after removing the above + Q_DISABLE_COPY(QQuickPointerDevice) }; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index c441cfc357..816c057ab0 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2117,8 +2117,10 @@ QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QQuickPointerDevic { // the list of devices should be very small so a linear search should be ok for (QQuickPointerEvent *e: pointerEventInstances) { - if (e->device() == device) + if (e->device() == device) { + device->m_event = e; return e; + } } QQuickPointerEvent *ev = nullptr; @@ -2136,6 +2138,7 @@ QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QQuickPointerDevic break; } pointerEventInstances << ev; + device->m_event = ev; return ev; } -- cgit v1.2.3 From defa2512a9128449e30d3b2c2fc42eaab201418b Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 9 May 2017 08:58:28 +0200 Subject: Disable loop peeling Loop peeling does in our current JIT not give us any measurable performance benefits (no measurable diff in any of the v8 benchmarks), and significantly increases the size of the generated JIT code. Change-Id: Icab7887300f9c1cd5891983cbfe5885fc2b4db91 Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4ssa.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp index 731c6ad38f..fc136b09ff 100644 --- a/src/qml/compiler/qv4ssa.cpp +++ b/src/qml/compiler/qv4ssa.cpp @@ -5417,7 +5417,11 @@ void Optimizer::run(QQmlEnginePrivate *qmlEngine, bool doTypeInference, bool pee showMeTheCode(function, "After loop detection"); // cfg2dot(function, loopDetection.allLoops()); - if (peelLoops) { + // ### disable loop peeling for now. It doesn't give any measurable performance + // improvements at this time, but significantly increases the size of the + // JIT generated code + Q_UNUSED(peelLoops); + if (0 && peelLoops) { QVector innerLoops = loopDetection.innermostLoops(); LoopPeeling(df).run(innerLoops); -- cgit v1.2.3 From e6acf80136db9f667d0d4664f6c68065355d6811 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Sat, 6 May 2017 22:32:57 +0200 Subject: QQuickWindow::createTextureFromImage(): return nullptr for null images Change-Id: Idf3315be104e058315d82893443e1c27d1d79f2e Reviewed-by: Laszlo Agocs --- src/quick/items/qquickwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index c441cfc357..ff4a357641 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -3862,7 +3862,7 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image, CreateTextureOptions options) const { Q_D(const QQuickWindow); - if (!isSceneGraphInitialized()) // check both for d->context and d->context->isValid() + if (!isSceneGraphInitialized() || image.isNull()) // check both for d->context and d->context->isValid() return 0; uint flags = 0; if (options & TextureCanUseAtlas) flags |= QSGRenderContext::CreateTexture_Atlas; -- cgit v1.2.3 From d214db97edcf7a76ecb629d49ac2e9dd0e1948e8 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Mon, 15 May 2017 16:01:34 +0200 Subject: Make all loaders synchronous in qquickdesigner support case It could result in wrong/strange behavior if the QtQuickDesigner shows asynchronous loaded items in the FormEditor view. So keep this setting disabled in that case. Change-Id: I9bbd75f33eb009e31064744564cc4104df624c3c Reviewed-by: Robin Burchell --- src/quick/designer/qquickdesignersupportitems.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src') diff --git a/src/quick/designer/qquickdesignersupportitems.cpp b/src/quick/designer/qquickdesignersupportitems.cpp index 2003b484ad..874faed0af 100644 --- a/src/quick/designer/qquickdesignersupportitems.cpp +++ b/src/quick/designer/qquickdesignersupportitems.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -79,6 +80,12 @@ static void stopAnimation(QObject *object) } } +static void makeLoaderSynchronous(QObject *object) +{ + if (QQuickLoader *loader = qobject_cast(object)) + loader->setAsynchronous(false); +} + static void allSubObjects(QObject *object, QObjectList &objectList) { // don't add null pointer and stop if the object is already in the list @@ -137,6 +144,7 @@ void QQuickDesignerSupportItems::tweakObjects(QObject *object) allSubObjects(object, objectList); for (QObject* childObject : qAsConst(objectList)) { stopAnimation(childObject); + makeLoaderSynchronous(childObject); if (fixResourcePathsForObjectCallBack) fixResourcePathsForObjectCallBack(childObject); } -- cgit v1.2.3 From 025bdeb66284c1016fb79c8ec230890542408320 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Tue, 9 May 2017 09:47:53 +0200 Subject: qmlcachegen: Fix MinGW developer build MinGW was unable to link qmlcachegen due to missing symbols from the autotest-exported class QV4::ExecutableAllocator. Introduce a separate Q_QML_AUTOTEST_EXPORT macro that is defined to empty when used by qmldevtools. Change-Id: Ib7f8984dd7617fae05bb4e1e6cc1fae0745ac3bc Reviewed-by: Simon Hausmann Reviewed-by: Oswald Buddenhagen --- src/qml/jsruntime/qv4executableallocator_p.h | 2 +- src/qml/qtqmlglobal_p.h | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/qml/jsruntime/qv4executableallocator_p.h b/src/qml/jsruntime/qv4executableallocator_p.h index f13f70e01a..353a6eacff 100644 --- a/src/qml/jsruntime/qv4executableallocator_p.h +++ b/src/qml/jsruntime/qv4executableallocator_p.h @@ -67,7 +67,7 @@ QT_BEGIN_NAMESPACE namespace QV4 { -class Q_AUTOTEST_EXPORT ExecutableAllocator +class Q_QML_AUTOTEST_EXPORT ExecutableAllocator { public: struct ChunkOfPages; diff --git a/src/qml/qtqmlglobal_p.h b/src/qml/qtqmlglobal_p.h index e9834ffc4c..3314e73d19 100644 --- a/src/qml/qtqmlglobal_p.h +++ b/src/qml/qtqmlglobal_p.h @@ -57,4 +57,10 @@ #define Q_QML_PRIVATE_EXPORT Q_QML_EXPORT +#if !defined(QT_QMLDEVTOOLS_LIB) && !defined(QT_BUILD_QMLDEVTOOLS_LIB) +# define Q_QML_AUTOTEST_EXPORT Q_AUTOTEST_EXPORT +#else +# define Q_QML_AUTOTEST_EXPORT +#endif + #endif // QTQMLGLOBAL_P_H -- cgit v1.2.3 From dc3015e891e903e6f701f1abc303be9f2efc2e5a Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Wed, 17 May 2017 09:42:00 +0200 Subject: Fix strings Change-Id: Ib073a4161f68bdde50c9287f66f111a49adb7e26 Reviewed-by: Leena Miettinen --- src/qml/types/qqmllistmodel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index eebf0f74d8..44d968fd46 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -1992,12 +1992,12 @@ void QQmlListModel::setDynamicRoles(bool enableDynamicRoles) if (m_mainThread && m_agent == 0) { if (enableDynamicRoles) { if (m_layout->roleCount()) - qmlWarning(this) << tr("unable to enable dynamic roles as this model is not empty!"); + qmlWarning(this) << tr("unable to enable dynamic roles as this model is not empty"); else m_dynamicRoles = true; } else { if (m_roles.count()) { - qmlWarning(this) << tr("unable to enable static roles as this model is not empty!"); + qmlWarning(this) << tr("unable to enable static roles as this model is not empty"); } else { m_dynamicRoles = false; } -- cgit v1.2.3 From 4425d6f9b637e7404379b611cef4b22929acbe62 Mon Sep 17 00:00:00 2001 From: Olivier JG Date: Fri, 16 Dec 2016 09:57:37 -0600 Subject: QML: emit messages when breaking bindings Add a new logging category that can be used to debug issues with unexpected binding breakages caused by assignment to previously bound properties. If enabled, outputs a message when a binding is broken with detailed location and property/value information. [ChangeLog][QtQml] The QML engine can now emit informational messages (in the "qt.qml.binding.removal" logging category) whenever a binding is lost due to an imperative assignment. This can be used to debug issues due to broken bindings. Change-Id: Ie31e5a198450b6f998c1266acfc97e8f33a92e3d Reviewed-by: Simon Hausmann --- .../src/qmllanguageref/syntax/propertybinding.qdoc | 20 +++++++++++++++++ src/qml/jsruntime/qv4qobjectwrapper.cpp | 20 +++++++++++++++-- src/qml/qml/qqmlvaluetypewrapper.cpp | 26 ++++++++++++++++++---- 3 files changed, 60 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/qml/doc/src/qmllanguageref/syntax/propertybinding.qdoc b/src/qml/doc/src/qmllanguageref/syntax/propertybinding.qdoc index f28ff5082a..99426d984d 100644 --- a/src/qml/doc/src/qmllanguageref/syntax/propertybinding.qdoc +++ b/src/qml/doc/src/qmllanguageref/syntax/propertybinding.qdoc @@ -179,6 +179,26 @@ Rectangle { Now, after the space key is pressed, the rectangle's height will continue auto-updating to always be three times its width. +\section3 Debugging overwriting of bindings + +A common cause of bugs in QML applications is accidentally overwriting bindings +with static values from JavaScript statements. To help developers track down +problems of this kind, the QML engine is able to emit messages whenever a +binding is lost due to imperative assignments. + +In order to generate such messages, you need to enable the informational output +for the \c{qt.qml.binding.removal} logging category, for instance by calling: + +\code +QLoggingCategory::setFilterRules(QStringLiteral("qt.qml.binding.removal.info=true")); +\endcode + +Please refer to the QLoggingCategory documentation for more information about +enabling output from logging categories. + +Note that is perfectly reasonable in some circumstances to overwrite bindings. +Any message generated by the QML engine should be treated as a diagnostic aid, +and not necessarily as evidence of a problem without further investigation. \section2 Using \c this with Property Binding diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index e4fb54f0ff..83730d632a 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -75,10 +75,13 @@ #include #include #include +#include #include QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcBindingRemoval, "qt.qml.binding.removal", QtWarningMsg) + // The code in this file does not violate strict aliasing, but GCC thinks it does // so turn off the warnings for us to have a clean build QT_WARNING_DISABLE_GCC("-Wstrict-aliasing") @@ -458,10 +461,23 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP } } - if (newBinding) + if (newBinding) { QQmlPropertyPrivate::setBinding(newBinding); - else + } else { + if (Q_UNLIKELY(lcBindingRemoval().isInfoEnabled())) { + if (auto binding = QQmlPropertyPrivate::binding(object, QQmlPropertyIndex(property->coreIndex()))) { + Q_ASSERT(!binding->isValueTypeProxy()); + const auto qmlBinding = static_cast(binding); + const auto stackFrame = engine->currentStackFrame(); + qCInfo(lcBindingRemoval, + "Overwriting binding on %s::%s at %s:%d that was initially bound at %s", + object->metaObject()->className(), qPrintable(property->name(object)), + qPrintable(stackFrame.source), stackFrame.line, + qPrintable(qmlBinding->expressionIdentifier())); + } + } QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(property->coreIndex())); + } if (!newBinding && property->isVarProperty()) { // allow assignment of "special" values (null, undefined, function) to var properties diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index d262b230e2..ce47ab9fa9 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -51,9 +51,11 @@ #include #include #include +#include QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcBindingRemoval) DEFINE_OBJECT_VTABLE(QV4::QQmlValueTypeWrapper); @@ -438,6 +440,9 @@ bool QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) if (reference) { QV4::ScopedFunctionObject f(scope, value); + const QQmlQPointer &referenceObject = reference->d()->object; + const int referencePropertyIndex = reference->d()->property; + if (f) { if (!f->isBinding()) { // assigning a JS function to a non-var-property is not allowed. @@ -452,18 +457,31 @@ bool QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) QQmlPropertyData cacheData; cacheData.setWritable(true); cacheData.setPropType(writeBackPropertyType); - cacheData.setCoreIndex(reference->d()->property); + cacheData.setCoreIndex(referencePropertyIndex); QV4::Scoped bindingFunction(scope, (const Value &)f); QV4::ScopedContext ctx(scope, bindingFunction->scope()); - QQmlBinding *newBinding = QQmlBinding::create(&cacheData, bindingFunction->function(), reference->d()->object, context, ctx); + QQmlBinding *newBinding = QQmlBinding::create(&cacheData, bindingFunction->function(), referenceObject, context, ctx); newBinding->setSourceLocation(bindingFunction->currentLocation()); - newBinding->setTarget(reference->d()->object, cacheData, pd); + newBinding->setTarget(referenceObject, cacheData, pd); QQmlPropertyPrivate::setBinding(newBinding); return true; } else { - QQmlPropertyPrivate::removeBinding(reference->d()->object, QQmlPropertyIndex(reference->d()->property, pd->coreIndex())); + if (Q_UNLIKELY(lcBindingRemoval().isInfoEnabled())) { + if (auto binding = QQmlPropertyPrivate::binding(referenceObject, QQmlPropertyIndex(referencePropertyIndex, pd->coreIndex()))) { + Q_ASSERT(!binding->isValueTypeProxy()); + const auto qmlBinding = static_cast(binding); + const auto stackFrame = v4->currentStackFrame(); + qCInfo(lcBindingRemoval, + "Overwriting binding on %s::%s which was initially bound at %s by setting \"%s\" at %s:%d", + referenceObject->metaObject()->className(), referenceObject->metaObject()->property(referencePropertyIndex).name(), + qPrintable(qmlBinding->expressionIdentifier()), + metaObject->property(pd->coreIndex()).name(), + qPrintable(stackFrame.source), stackFrame.line); + } + } + QQmlPropertyPrivate::removeBinding(referenceObject, QQmlPropertyIndex(referencePropertyIndex, pd->coreIndex())); } } -- cgit v1.2.3 From accaf69fc4f1d1197adae3deb2196897aa85aa43 Mon Sep 17 00:00:00 2001 From: Thomas McGuire Date: Tue, 16 May 2017 22:04:05 +0200 Subject: Fix QML Connections element ignoring the enabled property The enabled property was ignored if it was set before componentComplete() was called. [ChangeLog][QtQml] Fixed the QML Connections element ignoring the initial state of the enabled property Change-Id: I40c92fcb30f0bb8ca406f248b3bde2fced5ab58f Reviewed-by: Simon Hausmann --- src/qml/types/qqmlconnections.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp index cbf0f69093..7607c19374 100644 --- a/src/qml/types/qqmlconnections.cpp +++ b/src/qml/types/qqmlconnections.cpp @@ -287,6 +287,7 @@ void QQmlConnections::connectSignals() int signalIndex = QQmlPropertyPrivate::get(prop)->signalIndex(); QQmlBoundSignal *signal = new QQmlBoundSignal(target, signalIndex, this, qmlEngine(this)); + signal->setEnabled(d->enabled); QQmlBoundSignalExpression *expression = ctxtdata ? new QQmlBoundSignalExpression(target, signalIndex, -- cgit v1.2.3 From 1e8f85dbc90da6b49fa7018ccbc35db68281d395 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Thu, 18 May 2017 16:59:02 +0200 Subject: don't try to pull in the regular config headers when bootstrapping Task-number: QTBUG-60675 Change-Id: I7ae9ab4f442d34f6eaa770652029b5dfccef346a Reviewed-by: Simon Hausmann --- src/qml/qtqmlglobal.h | 16 +++++++++------- src/qml/qtqmlglobal_p.h | 4 +++- 2 files changed, 12 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/qml/qtqmlglobal.h b/src/qml/qtqmlglobal.h index 6704e55b52..387c28a945 100644 --- a/src/qml/qtqmlglobal.h +++ b/src/qml/qtqmlglobal.h @@ -40,18 +40,20 @@ #ifndef QTQMLGLOBAL_H #define QTQMLGLOBAL_H +#if defined(QT_BUILD_QMLDEVTOOLS_LIB) || defined(QT_QMLDEVTOOLS_LIB) +# define QT_QML_BOOTSTRAPPED +#endif + #include -#include -#if QT_CONFIG(qml_network) -#include +#ifndef QT_QML_BOOTSTRAPPED +# include +# if QT_CONFIG(qml_network) +# include +# endif #endif QT_BEGIN_NAMESPACE -#if defined(QT_BUILD_QMLDEVTOOLS_LIB) || defined(QT_QMLDEVTOOLS_LIB) -# define QT_QML_BOOTSTRAPPED -#endif - #if !defined(QT_QML_BOOTSTRAPPED) && !defined(QT_STATIC) # if defined(QT_BUILD_QML_LIB) # define Q_QML_EXPORT Q_DECL_EXPORT diff --git a/src/qml/qtqmlglobal_p.h b/src/qml/qtqmlglobal_p.h index e9834ffc4c..0313d8e33b 100644 --- a/src/qml/qtqmlglobal_p.h +++ b/src/qml/qtqmlglobal_p.h @@ -52,8 +52,10 @@ // #include -#include #include +#ifndef QT_QML_BOOTSTRAPPED +# include +#endif #define Q_QML_PRIVATE_EXPORT Q_QML_EXPORT -- cgit v1.2.3 From b078939cb86c7fd82335f4d4a815b6f62eb7b26f Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Thu, 18 May 2017 12:01:47 +0200 Subject: Revert "QQuickItem: Port a number of manual loops to range-for" This reverts commit 8d92e595e0e8cf19f60db2fce4a543265c9130e9. This is broken in a few places, e.g. setEffectiveVisibleRecur emits, which means that it may run uncontrolled code, so we can't be sure the list isn't altered underneath us. Change-Id: I58b8b62e74581207c1b14902ea7b8b552761de8a Task-number: QTBUG-58811 Reviewed-by: Lars Knoll Reviewed-by: Simon Hausmann Reviewed-by: Shawn Rutledge --- src/quick/items/qquickitem.cpp | 90 +++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 8e90827a3d..e280b0bd61 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -146,8 +146,8 @@ QQuickTransform::QQuickTransform(QQuickTransformPrivate &dd, QObject *parent) QQuickTransform::~QQuickTransform() { Q_D(QQuickTransform); - for (QQuickItem *item : qAsConst(d->items)) { - QQuickItemPrivate *p = QQuickItemPrivate::get(item); + for (int ii = 0; ii < d->items.count(); ++ii) { + QQuickItemPrivate *p = QQuickItemPrivate::get(d->items.at(ii)); p->transforms.removeOne(this); p->dirty(QQuickItemPrivate::Transform); } @@ -156,8 +156,8 @@ QQuickTransform::~QQuickTransform() void QQuickTransform::update() { Q_D(QQuickTransform); - for (QQuickItem *item : qAsConst(d->items)) { - QQuickItemPrivate *p = QQuickItemPrivate::get(item); + for (int ii = 0; ii < d->items.count(); ++ii) { + QQuickItemPrivate *p = QQuickItemPrivate::get(d->items.at(ii)); p->dirty(QQuickItemPrivate::Transform); } } @@ -169,7 +169,9 @@ QQuickContents::QQuickContents(QQuickItem *item) QQuickContents::~QQuickContents() { - for (QQuickItem *child : m_item->childItems()) { + QList children = m_item->childItems(); + for (int i = 0; i < children.count(); ++i) { + QQuickItem *child = children.at(i); QQuickItemPrivate::get(child)->removeItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed); } } @@ -192,8 +194,9 @@ bool QQuickContents::calcHeight(QQuickItem *changed) } else { qreal top = std::numeric_limits::max(); qreal bottom = -std::numeric_limits::max(); - const QList children = m_item->childItems(); - for (QQuickItem *child : qAsConst(children)) { + QList children = m_item->childItems(); + for (int i = 0; i < children.count(); ++i) { + QQuickItem *child = children.at(i); qreal y = child->y(); if (y + child->height() > bottom) bottom = y + child->height(); @@ -226,8 +229,9 @@ bool QQuickContents::calcWidth(QQuickItem *changed) } else { qreal left = std::numeric_limits::max(); qreal right = -std::numeric_limits::max(); - const QList children = m_item->childItems(); - for (QQuickItem *child : qAsConst(children)) { + QList children = m_item->childItems(); + for (int i = 0; i < children.count(); ++i) { + QQuickItem *child = children.at(i); qreal x = child->x(); if (x + child->width() > right) right = x + child->width(); @@ -246,7 +250,9 @@ void QQuickContents::complete() { QQuickItemPrivate::get(m_item)->addItemChangeListener(this, QQuickItemPrivate::Children); - for (QQuickItem *child : m_item->childItems()) { + QList children = m_item->childItems(); + for (int i = 0; i < children.count(); ++i) { + QQuickItem *child = children.at(i); QQuickItemPrivate::get(child)->addItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed); //###what about changes to visibility? } @@ -1347,7 +1353,8 @@ void QQuickKeysAttached::componentComplete() #if QT_CONFIG(im) Q_D(QQuickKeysAttached); if (d->item) { - for (QQuickItem *targetItem : qAsConst(d->targets)) { + for (int ii = 0; ii < d->targets.count(); ++ii) { + QQuickItem *targetItem = d->targets.at(ii); if (targetItem && (targetItem->flags() & QQuickItem::ItemAcceptsInputMethod)) { d->item->setFlag(QQuickItem::ItemAcceptsInputMethod); break; @@ -1369,10 +1376,11 @@ void QQuickKeysAttached::keyPressed(QKeyEvent *event, bool post) // first process forwards if (d->item && d->item->window()) { d->inPress = true; - for (QQuickItem *targetItem : qAsConst(d->targets)) { - if (targetItem && targetItem->isVisible()) { + for (int ii = 0; ii < d->targets.count(); ++ii) { + QQuickItem *i = d->targets.at(ii); + if (i && i->isVisible()) { event->accept(); - QCoreApplication::sendEvent(targetItem, event); + QCoreApplication::sendEvent(i, event); if (event->isAccepted()) { d->inPress = false; return; @@ -1412,10 +1420,11 @@ void QQuickKeysAttached::keyReleased(QKeyEvent *event, bool post) if (d->item && d->item->window()) { d->inRelease = true; - for (QQuickItem *targetItem : qAsConst(d->targets)) { - if (targetItem && targetItem->isVisible()) { + for (int ii = 0; ii < d->targets.count(); ++ii) { + QQuickItem *i = d->targets.at(ii); + if (i && i->isVisible()) { event->accept(); - QCoreApplication::sendEvent(targetItem, event); + QCoreApplication::sendEvent(i, event); if (event->isAccepted()) { d->inRelease = false; return; @@ -1439,7 +1448,8 @@ void QQuickKeysAttached::inputMethodEvent(QInputMethodEvent *event, bool post) Q_D(QQuickKeysAttached); if (post == m_processPost && d->item && !d->inIM && d->item->window()) { d->inIM = true; - for (QQuickItem *targetItem : qAsConst(d->targets)) { + for (int ii = 0; ii < d->targets.count(); ++ii) { + QQuickItem *targetItem = d->targets.at(ii); if (targetItem && targetItem->isVisible() && (targetItem->flags() & QQuickItem::ItemAcceptsInputMethod)) { QCoreApplication::sendEvent(targetItem, event); if (event->isAccepted()) { @@ -1458,12 +1468,13 @@ QVariant QQuickKeysAttached::inputMethodQuery(Qt::InputMethodQuery query) const { Q_D(const QQuickKeysAttached); if (d->item) { - for (QQuickItem *targetItem : qAsConst(d->targets)) { - if (targetItem && targetItem->isVisible() && (targetItem->flags() & QQuickItem::ItemAcceptsInputMethod) && targetItem == d->imeItem) { - //### how robust is targetItem == d->imeItem check? - QVariant v = targetItem->inputMethodQuery(query); + for (int ii = 0; ii < d->targets.count(); ++ii) { + QQuickItem *i = d->targets.at(ii); + if (i && i->isVisible() && (i->flags() & QQuickItem::ItemAcceptsInputMethod) && i == d->imeItem) { + //### how robust is i == d->imeItem check? + QVariant v = i->inputMethodQuery(query); if (v.userType() == QVariant::RectF) - v = d->item->mapRectFromItem(targetItem, v.toRectF()); //### cost? + v = d->item->mapRectFromItem(i, v.toRectF()); //### cost? return v; } } @@ -1637,9 +1648,11 @@ void QQuickItemPrivate::setImplicitLayoutMirror(bool mirror, bool inherit) if (isMirrorImplicit) setLayoutMirror(inherit ? inheritedLayoutMirror : false); - for (QQuickItem *child : qAsConst(childItems)) { - QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child); - childPrivate->setImplicitLayoutMirror(inheritedLayoutMirror, inheritMirrorFromParent); + for (int i = 0; i < childItems.count(); ++i) { + if (QQuickItem *child = qmlobject_cast(childItems.at(i))) { + QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child); + childPrivate->setImplicitLayoutMirror(inheritedLayoutMirror, inheritMirrorFromParent); + } } } @@ -2395,7 +2408,8 @@ QQuickItem::~QQuickItem() remove themselves from our list of transforms when that list has already been destroyed after ~QQuickItem() has run. */ - for (QQuickTransform *t : qAsConst(d->transforms)) { + for (int ii = 0; ii < d->transforms.count(); ++ii) { + QQuickTransform *t = d->transforms.at(ii); QQuickTransformPrivate *tp = QQuickTransformPrivate::get(t); tp->items.removeOne(this); } @@ -2886,8 +2900,8 @@ QList QQuickItemPrivate::paintOrderChildItems() const // If none of the items have set Z then the paint order list is the same as // the childItems list. This is by far the most common case. bool haveZ = false; - for (QQuickItem *childItem : qAsConst(childItems)) { - if (QQuickItemPrivate::get(childItem)->z() != 0.) { + for (int i = 0; i < childItems.count(); ++i) { + if (QQuickItemPrivate::get(childItems.at(i))->z() != 0.) { haveZ = true; break; } @@ -2988,7 +3002,8 @@ void QQuickItemPrivate::refWindow(QQuickWindow *c) if (!parentItem) QQuickWindowPrivate::get(window)->parentlessItems.insert(q); - for (QQuickItem *child : qAsConst(childItems)) { + for (int ii = 0; ii < childItems.count(); ++ii) { + QQuickItem *child = childItems.at(ii); QQuickItemPrivate::get(child)->refWindow(c); } @@ -3040,7 +3055,8 @@ void QQuickItemPrivate::derefWindow() paintNode = 0; - for (QQuickItem *child : qAsConst(childItems)) { + for (int ii = 0; ii < childItems.count(); ++ii) { + QQuickItem *child = childItems.at(ii); QQuickItemPrivate::get(child)->derefWindow(); } @@ -3485,7 +3501,8 @@ void QQuickItemPrivate::transform_clear(QQmlListProperty *prop) QQuickItem *that = static_cast(prop->object); QQuickItemPrivate *p = QQuickItemPrivate::get(that); - for (QQuickTransform *t : qAsConst(p->transforms)) { + for (int ii = 0; ii < p->transforms.count(); ++ii) { + QQuickTransform *t = p->transforms.at(ii); QQuickTransformPrivate *tp = QQuickTransformPrivate::get(t); tp->items.removeOne(that); } @@ -5850,9 +5867,8 @@ bool QQuickItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible) } bool childVisibilityChanged = false; - for (QQuickItem *childItem : qAsConst(childItems)) { - childVisibilityChanged |= QQuickItemPrivate::get(childItem)->setEffectiveVisibleRecur(newEffectiveVisible); - } + for (int ii = 0; ii < childItems.count(); ++ii) + childVisibilityChanged |= QQuickItemPrivate::get(childItems.at(ii))->setEffectiveVisibleRecur(newEffectiveVisible); itemChange(QQuickItem::ItemVisibleHasChanged, effectiveVisible); #if QT_CONFIG(accessibility) @@ -5901,8 +5917,8 @@ void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffec } } - for (QQuickItem *childItem : qAsConst(childItems)) { - QQuickItemPrivate::get(childItem)->setEffectiveEnableRecur( + for (int ii = 0; ii < childItems.count(); ++ii) { + QQuickItemPrivate::get(childItems.at(ii))->setEffectiveEnableRecur( (flags & QQuickItem::ItemIsFocusScope) && scope ? q : scope, newEffectiveEnable); } -- cgit v1.2.3 From 2affe19182b99d8d1c9655fa0c58c8af3e0b9506 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 16 May 2017 12:42:42 +0200 Subject: Free up completely empty Chunks, and return the memory to the OS Detect any Chunk that's completely empty, deallocate it and return the memory to the OS (as far as that's supported). Change-Id: I6b6a77f2cdf478cbf16aad30a9cae37c98c6500e Reviewed-by: Simon Hausmann --- src/qml/memory/qv4mm.cpp | 30 ++++++++++++++++++++++-------- src/qml/memory/qv4mmdefs_p.h | 6 +++++- 2 files changed, 27 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 0a6dfb9170..ef36e62373 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -275,8 +275,9 @@ QString binary(quintptr) { return QString(); } #define SDUMP if (1) ; else qDebug #endif -void Chunk::sweep() +bool Chunk::sweep() { + bool hasUsedSlots = false; SDUMP() << "sweeping chunk" << this; HeapItem *o = realBase(); bool lastSlotFree = false; @@ -316,6 +317,7 @@ void Chunk::sweep() } } objectBitmap[i] = blackBitmap[i]; + hasUsedSlots |= (blackBitmap[i] != 0); blackBitmap[i] = 0; extendsBitmap[i] = e; lastSlotFree = !((objectBitmap[i]|extendsBitmap[i]) >> (sizeof(quintptr)*8 - 1)); @@ -325,6 +327,7 @@ void Chunk::sweep() o += Chunk::Bits; } // DEBUG << "swept chunk" << this << "freed" << slotsFreed << "slots."; + return hasUsedSlots; } void Chunk::freeAll() @@ -579,12 +582,21 @@ void BlockAllocator::sweep() // qDebug() << "BlockAlloc: sweep"; usedSlotsAfterLastSweep = 0; - for (auto c : chunks) { - c->sweep(); - c->sortIntoBins(freeBins, NumBins); -// qDebug() << "used slots in chunk" << c << ":" << c->nUsedSlots(); - usedSlotsAfterLastSweep += c->nUsedSlots(); - } + + auto isFree = [this] (Chunk *c) { + bool isUsed = c->sweep(); + + if (isUsed) { + c->sortIntoBins(freeBins, NumBins); + usedSlotsAfterLastSweep += c->nUsedSlots(); + } else { + chunkAllocator->free(c); + } + return !isUsed; + }; + + auto newEnd = std::remove_if(chunks.begin(), chunks.end(), isFree); + chunks.erase(newEnd, chunks.end()); } void BlockAllocator::freeAll() @@ -950,7 +962,8 @@ void MemoryManager::runGC() #ifndef QT_NO_DEBUG qDebug() << " Triggered by alloc request of" << lastAllocRequestedSlots << "slots."; #endif - qDebug() << "Allocated" << totalMem << "bytes in" << blockAllocator.chunks.size() << "chunks"; + size_t oldChunks = blockAllocator.chunks.size(); + qDebug() << "Allocated" << totalMem << "bytes in" << oldChunks << "chunks"; qDebug() << "Fragmented memory before GC" << (totalMem - usedBefore); dumpBins(&blockAllocator); @@ -975,6 +988,7 @@ void MemoryManager::runGC() qDebug() << "Used memory before GC:" << usedBefore; qDebug() << "Used memory after GC:" << usedAfter; qDebug() << "Freed up bytes:" << (usedBefore - usedAfter); + qDebug() << "Freed up chunks:" << (oldChunks - blockAllocator.chunks.size()); size_t lost = blockAllocator.allocatedMem() - memInBins - usedAfter; if (lost) qDebug() << "!!!!!!!!!!!!!!!!!!!!! LOST MEM:" << lost << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"; diff --git a/src/qml/memory/qv4mmdefs_p.h b/src/qml/memory/qv4mmdefs_p.h index db0ffe11a2..6e820dfc0f 100644 --- a/src/qml/memory/qv4mmdefs_p.h +++ b/src/qml/memory/qv4mmdefs_p.h @@ -59,6 +59,10 @@ QT_BEGIN_NAMESPACE namespace QV4 { +struct MarkStack; + +typedef void(*ClassDestroyStatsCallback)(const char *); + /* * Chunks are the basic structure containing GC managed objects. * @@ -175,7 +179,7 @@ struct Chunk { return usedSlots; } - void sweep(); + bool sweep(); void freeAll(); void sortIntoBins(HeapItem **bins, uint nBins); -- cgit v1.2.3 From 7ed93899e9305ccd538361ee58baa4bf15ff8a41 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 10 May 2017 09:42:18 +0200 Subject: Optimizations to the generated byte code Cut the size of the generated byte code in half, by storing parameters in a more compact form and always storing the instruction type in the instruction. We can still used computed goto's for a fast interpreter, by looking up the jump point for the next instruction in the jump table. Another advantage is that the byte code is now platform independent (modulo endianness). The change comes with a 3% performance impact on x86_64, which is acceptable considering the size savings on the bytecode. Change-Id: I37de3e1f94611987a85e65ea86536583aa965d6d Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4instr_moth_p.h | 10 +++---- src/qml/compiler/qv4isel_moth.cpp | 55 -------------------------------------- src/qml/jsruntime/qv4vme_moth.cpp | 32 +++++----------------- src/qml/jsruntime/qv4vme_moth_p.h | 10 +------ 4 files changed, 10 insertions(+), 97 deletions(-) (limited to 'src') diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index fbd6ac8f99..dabda7bae8 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -170,11 +170,7 @@ QT_BEGIN_NAMESPACE #define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QV4::Moth::Instr) - 1) -#ifdef MOTH_THREADED_INTERPRETER -# define MOTH_INSTR_HEADER union { quint32 instructionType; void *code; }; -#else -# define MOTH_INSTR_HEADER quint32 instructionType; -#endif +#define MOTH_INSTR_HEADER quint32 instructionType; #define MOTH_INSTR_ENUM(I, FMT) I, #define MOTH_INSTR_SIZE(I, FMT) ((sizeof(QV4::Moth::Instr::instr_##FMT) + MOTH_INSTR_ALIGN_MASK) & ~MOTH_INSTR_ALIGN_MASK) @@ -194,8 +190,8 @@ struct Param { // Arg(outer): 4 // Local(outer): 5 // ... - unsigned scope; - unsigned index; + unsigned scope : 12; + unsigned index : 20; bool isConstant() const { return !scope; } bool isArgument() const { return scope >= 2 && !(scope &1); } diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index dc6fe317e5..fb805dce02 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -1436,29 +1436,6 @@ CompilationUnit::~CompilationUnit() void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine) { -#ifdef MOTH_THREADED_INTERPRETER - // link byte code against addresses of instructions - for (int i = 0; i < codeRefs.count(); ++i) { - QByteArray &codeRef = codeRefs[i]; - char *code = codeRef.data(); - int index = 0; - while (index < codeRef.size()) { - Instr *genericInstr = reinterpret_cast(code + index); - - switch (genericInstr->common.instructionType) { -#define LINK_INSTRUCTION(InstructionType, Member) \ - case Instr::InstructionType: \ - genericInstr->common.code = VME::instructionJumpTable()[static_cast(genericInstr->common.instructionType)]; \ - index += InstrMeta<(int)Instr::InstructionType>::Size; \ - break; - - FOR_EACH_MOTH_INSTR(LINK_INSTRUCTION) - - } - } - } -#endif - runtimeFunctions.resize(data->functionTableSize); runtimeFunctions.fill(0); for (int i = 0 ;i < runtimeFunctions.size(); ++i) { @@ -1516,17 +1493,6 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit QByteArray padding; -#if defined(MOTH_THREADED_INTERPRETER) && !defined(V4_BOOTSTRAP) - // Map from instruction label back to instruction type. Only needed when persisting - // already linked compilation units; - QHash reverseInstructionMapping; - if (engine) { - void **instructions = VME::instructionJumpTable(); - for (int i = 0; i < Instr::LastInstruction; ++i) - reverseInstructionMapping.insert(instructions[i], i); - } -#endif - for (int i = 0; i < codeRefs.size(); ++i) { const CompiledData::Function *compiledFunction = unit->functionAt(i); @@ -1545,27 +1511,6 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit QByteArray code = codeRefs.at(i); -#if defined(MOTH_THREADED_INTERPRETER) && !defined(V4_BOOTSTRAP) - if (!reverseInstructionMapping.isEmpty()) { - char *codePtr = code.data(); // detaches - int index = 0; - while (index < code.size()) { - Instr *genericInstr = reinterpret_cast(codePtr + index); - - genericInstr->common.instructionType = reverseInstructionMapping.value(genericInstr->common.code); - - switch (genericInstr->common.instructionType) { - #define REVERSE_INSTRUCTION(InstructionType, Member) \ - case Instr::InstructionType: \ - index += InstrMeta<(int)Instr::InstructionType>::Size; \ - break; - - FOR_EACH_MOTH_INSTR(REVERSE_INSTRUCTION) - } - } - } -#endif - written = device->write(code.constData(), compiledFunction->codeSize); if (written != qint64(compiledFunction->codeSize)) { *errorString = device->errorString(); diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index a74016ab0c..2288d9ef0a 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -304,7 +304,7 @@ using namespace QV4::Moth; # define MOTH_END_INSTR(I) } \ genericInstr = reinterpret_cast(code); \ - goto *genericInstr->common.code; \ + goto *jumpTable[genericInstr->common.instructionType]; \ #else @@ -356,11 +356,7 @@ Param traceParam(const Param ¶m) if (engine->hasException) \ goto catchException -QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code -#ifdef MOTH_THREADED_INTERPRETER - , void ***storeJumpTable -#endif - ) +QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code) { #ifdef DO_TRACE_INSTR qDebug("Starting VME with context=%p and code=%p", context, code); @@ -369,15 +365,11 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code qt_v4ResolvePendingBreakpointsHook(); #ifdef MOTH_THREADED_INTERPRETER - if (storeJumpTable) { #define MOTH_INSTR_ADDR(I, FMT) &&op_##I, - static void *jumpTable[] = { - FOR_EACH_MOTH_INSTR(MOTH_INSTR_ADDR) - }; + static void *jumpTable[] = { + FOR_EACH_MOTH_INSTR(MOTH_INSTR_ADDR) + }; #undef MOTH_INSTR_ADDR - *storeJumpTable = jumpTable; - return QV4::Primitive::undefinedValue().asReturnedValue(); - } #endif QV4::Value *stack = 0; @@ -428,7 +420,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code for (;;) { const Instr *genericInstr = reinterpret_cast(code); #ifdef MOTH_THREADED_INTERPRETER - goto *genericInstr->common.code; + goto *jumpTable[genericInstr->common.instructionType]; #else switch (genericInstr->common.instructionType) { #endif @@ -960,18 +952,6 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code } } -#ifdef MOTH_THREADED_INTERPRETER -void **VME::instructionJumpTable() -{ - static void **jumpTable = 0; - if (!jumpTable) { - const uchar *code = 0; - VME().run(0, code, &jumpTable); - } - return jumpTable; -} -#endif - QV4::ReturnedValue VME::exec(ExecutionEngine *engine, const uchar *code) { VME vme; diff --git a/src/qml/jsruntime/qv4vme_moth_p.h b/src/qml/jsruntime/qv4vme_moth_p.h index f8893509d9..8d46207f2b 100644 --- a/src/qml/jsruntime/qv4vme_moth_p.h +++ b/src/qml/jsruntime/qv4vme_moth_p.h @@ -67,16 +67,8 @@ class VME public: static QV4::ReturnedValue exec(QV4::ExecutionEngine *, const uchar *); -#ifdef MOTH_THREADED_INTERPRETER - static void **instructionJumpTable(); -#endif - private: - QV4::ReturnedValue run(QV4::ExecutionEngine *, const uchar *code -#ifdef MOTH_THREADED_INTERPRETER - , void ***storeJumpTable = 0 -#endif - ); + QV4::ReturnedValue run(QV4::ExecutionEngine *, const uchar *code); }; } // namespace Moth -- cgit v1.2.3 From 31d6333fa1bdabf4711739e3399a4fa58f8291a0 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 11 May 2017 09:12:54 +0200 Subject: Smaller cleanups Don't store the current context in Moth's run() method, instead always retrieve it through the engine. Change-Id: I9d56f2c93a02fc1e2f03839b14b3c0053d60b6b2 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4vme_moth.cpp | 40 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 2288d9ef0a..d662b1738d 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -378,7 +378,6 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code) const uchar *exceptionHandler = 0; QV4::Scope scope(engine); - QV4::ExecutionContext *context = engine->currentContext; engine->current->lineNumber = -1; #ifdef DO_TRACE_INSTR @@ -388,7 +387,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code) // setup lookup scopes int scopeDepth = 0; { - QV4::Heap::ExecutionContext *scope = context->d(); + QV4::Heap::ExecutionContext *scope = engine->current; while (scope) { ++scopeDepth; scope = scope->outer; @@ -397,10 +396,10 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code) Q_ALLOCA_VAR(QV4::Value*, scopes, sizeof(QV4::Value *)*(2 + 2*scopeDepth)); { - scopes[0] = const_cast(static_cast(context->d()->compilationUnit)->constants); + scopes[0] = const_cast(static_cast(engine->current->compilationUnit)->constants); // stack gets setup in push instruction scopes[1] = 0; - QV4::Heap::ExecutionContext *scope = context->d(); + QV4::Heap::ExecutionContext *scope = engine->current; int i = 0; while (scope) { if (scope->type >= QV4::Heap::ExecutionContext::Type_SimpleCallContext) { @@ -439,12 +438,12 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code) MOTH_BEGIN_INSTR(LoadRuntimeString) // TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData()); - VALUE(instr.result) = context->d()->compilationUnit->runtimeStrings[instr.stringId]; + VALUE(instr.result) = engine->current->compilationUnit->runtimeStrings[instr.stringId]; MOTH_END_INSTR(LoadRuntimeString) MOTH_BEGIN_INSTR(LoadRegExp) // TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData()); - VALUE(instr.result) = static_cast(context->d()->compilationUnit)->runtimeRegularExpressions[instr.regExpId]; + VALUE(instr.result) = static_cast(engine->current->compilationUnit)->runtimeRegularExpressions[instr.regExpId]; MOTH_END_INSTR(LoadRegExp) MOTH_BEGIN_INSTR(LoadClosure) @@ -457,7 +456,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code) MOTH_END_INSTR(LoadName) MOTH_BEGIN_INSTR(GetGlobalLookup) - QV4::Lookup *l = context->d()->lookups + instr.index; + QV4::Lookup *l = engine->current->lookups + instr.index; STOREVALUE(instr.result, l->globalGetter(l, engine)); MOTH_END_INSTR(GetGlobalLookup) @@ -472,7 +471,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code) MOTH_END_INSTR(LoadElement) MOTH_BEGIN_INSTR(LoadElementLookup) - QV4::Lookup *l = context->d()->lookups + instr.lookup; + QV4::Lookup *l = engine->current->lookups + instr.lookup; STOREVALUE(instr.result, l->indexedGetter(l, VALUE(instr.base), VALUE(instr.index))); MOTH_END_INSTR(LoadElementLookup) @@ -482,7 +481,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code) MOTH_END_INSTR(StoreElement) MOTH_BEGIN_INSTR(StoreElementLookup) - QV4::Lookup *l = context->d()->lookups + instr.lookup; + QV4::Lookup *l = engine->current->lookups + instr.lookup; l->indexedSetter(l, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source)); CHECK_EXCEPTION; MOTH_END_INSTR(StoreElementLookup) @@ -492,7 +491,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code) MOTH_END_INSTR(LoadProperty) MOTH_BEGIN_INSTR(GetLookup) - QV4::Lookup *l = context->d()->lookups + instr.index; + QV4::Lookup *l = engine->current->lookups + instr.index; STOREVALUE(instr.result, l->getter(l, engine, VALUE(instr.base))); MOTH_END_INSTR(GetLookup) @@ -502,7 +501,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code) MOTH_END_INSTR(StoreProperty) MOTH_BEGIN_INSTR(SetLookup) - QV4::Lookup *l = context->d()->lookups + instr.index; + QV4::Lookup *l = engine->current->lookups + instr.index; l->setter(l, engine, VALUE(instr.base), VALUE(instr.source)); CHECK_EXCEPTION; MOTH_END_INSTR(SetLookup) @@ -573,7 +572,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code) MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallProperty) - TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData()); + TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(engine)->toQString().toUtf8().constData()); Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast(stack + instr.callData); callData->tag = quint32(Value::ValueTypeInternal::Integer); @@ -592,7 +591,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code) MOTH_END_INSTR(CallPropertyLookup) MOTH_BEGIN_INSTR(CallScopeObjectProperty) - TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData()); + TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(engine)->toQString().toUtf8().constData()); Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast(stack + instr.callData); callData->tag = quint32(Value::ValueTypeInternal::Integer); @@ -602,7 +601,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code) MOTH_END_INSTR(CallScopeObjectProperty) MOTH_BEGIN_INSTR(CallContextObjectProperty) - TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData()); + TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(engine)->toQString().toUtf8().constData()); Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast(stack + instr.callData); callData->tag = quint32(Value::ValueTypeInternal::Integer); @@ -653,18 +652,15 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code) MOTH_BEGIN_INSTR(CallBuiltinPushCatchScope) Runtime::method_pushCatchScope(static_cast(engine), instr.name); - context = engine->currentContext; MOTH_END_INSTR(CallBuiltinPushCatchScope) MOTH_BEGIN_INSTR(CallBuiltinPushScope) Runtime::method_pushWithScope(VALUE(instr.arg), static_cast(engine)); - context = engine->currentContext; CHECK_EXCEPTION; MOTH_END_INSTR(CallBuiltinPushScope) MOTH_BEGIN_INSTR(CallBuiltinPopScope) Runtime::method_popScope(static_cast(engine)); - context = engine->currentContext; MOTH_END_INSTR(CallBuiltinPopScope) MOTH_BEGIN_INSTR(CallBuiltinForeachIteratorObject) @@ -904,22 +900,22 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code) #ifndef QT_NO_QML_DEBUGGER MOTH_BEGIN_INSTR(Debug) engine->current->lineNumber = instr.lineNumber; - QV4::Debugging::Debugger *debugger = context->engine()->debugger(); + QV4::Debugging::Debugger *debugger = engine->debugger(); if (debugger && debugger->pauseAtNextOpportunity()) debugger->maybeBreakAtInstruction(); if (qt_v4IsDebugging) - qt_v4CheckForBreak(context); + qt_v4CheckForBreak(engine->currentContext); MOTH_END_INSTR(Debug) MOTH_BEGIN_INSTR(Line) engine->current->lineNumber = instr.lineNumber; if (qt_v4IsDebugging) - qt_v4CheckForBreak(context); + qt_v4CheckForBreak(engine->currentContext); MOTH_END_INSTR(Line) #endif // QT_NO_QML_DEBUGGER MOTH_BEGIN_INSTR(LoadThis) - VALUE(instr.result) = context->thisObject(); + VALUE(instr.result) = engine->currentContext->thisObject(); MOTH_END_INSTR(LoadThis) MOTH_BEGIN_INSTR(LoadQmlContext) @@ -945,7 +941,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code) Q_ASSERT(false); catchException: - Q_ASSERT(context->engine()->hasException); + Q_ASSERT(engine->hasException); if (!exceptionHandler) return QV4::Encode::undefined(); code = exceptionHandler; -- cgit v1.2.3 From fa4f49169ad9e7e4afc934b3c947936bf0fcafdc Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 12 May 2017 09:33:25 +0200 Subject: Move a few more members from ExecutionEngine to EngineBase Change-Id: I5d1e0d2251e04cc871f9c298849aafac17f23fbf Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4engine.cpp | 4 +--- src/qml/jsruntime/qv4engine_p.h | 11 ----------- src/qml/memory/qv4mmdefs_p.h | 8 ++++++++ 3 files changed, 9 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 0b61c6e7e6..b72e1d9dcf 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -130,10 +130,8 @@ QQmlEngine *ExecutionEngine::qmlEngine() const qint32 ExecutionEngine::maxCallDepth = -1; ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) - : callDepth(0) - , executableAllocator(new QV4::ExecutableAllocator) + : executableAllocator(new QV4::ExecutableAllocator) , regExpAllocator(new QV4::ExecutableAllocator) - , currentContext(0) , bumperPointerAllocator(new WTF::BumpPointerAllocator) , jsStack(new WTF::PageAllocation) , globalCode(0) diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 5182f24235..a57456c0fb 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -96,21 +96,14 @@ private: friend struct ExecutionContext; friend struct Heap::ExecutionContext; public: - qint32 callDepth; - ExecutableAllocator *executableAllocator; ExecutableAllocator *regExpAllocator; QScopedPointer iselFactory; - ExecutionContext *currentContext; - - Value *jsStackLimit; - WTF::BumpPointerAllocator *bumperPointerAllocator; // Used by Yarr Regex engine. enum { JSStackLimit = 4*1024*1024 }; WTF::PageAllocation *jsStack; - Value *jsStackBase; void pushForGC(Heap::Base *m) { *jsStackTop = m; @@ -129,10 +122,6 @@ public: return ptr; } - IdentifierTable *identifierTable; - - Object *globalObject; - Function *globalCode; #ifdef V4_BOOTSTRAP diff --git a/src/qml/memory/qv4mmdefs_p.h b/src/qml/memory/qv4mmdefs_p.h index 6e820dfc0f..75cf4681d6 100644 --- a/src/qml/memory/qv4mmdefs_p.h +++ b/src/qml/memory/qv4mmdefs_p.h @@ -275,6 +275,14 @@ struct EngineBase { #endif MemoryManager *memoryManager = 0; Runtime runtime; + + qint32 callDepth = 0; + Value *jsStackLimit = 0; + Value *jsStackBase = 0; + + ExecutionContext *currentContext = 0; + IdentifierTable *identifierTable = 0; + Object *globalObject = 0; }; #if defined(Q_CC_MSVC) || defined(Q_CC_GNU) #pragma pack(pop) -- cgit v1.2.3 From c83685bf3ae1c85cf204e0cbf7fc9b5db819a0f5 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 12 May 2017 09:40:03 +0200 Subject: Move the EngineBase class into it's own header file Change-Id: Idf87618e4ebff99f3b3c269c950191d67a0182b2 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/jsruntime.pri | 1 + src/qml/jsruntime/qv4engine_p.h | 1 + src/qml/jsruntime/qv4enginebase_p.h | 99 +++++++++++++++++++++++++++++++++++++ src/qml/memory/qv4mmdefs_p.h | 35 ------------- 4 files changed, 101 insertions(+), 35 deletions(-) create mode 100644 src/qml/jsruntime/qv4enginebase_p.h (limited to 'src') diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index 76ac8d4a91..9938f60aea 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -48,6 +48,7 @@ HEADERS += \ $$PWD/qv4global_p.h \ $$PWD/qv4alloca_p.h \ $$PWD/qv4engine_p.h \ + $$PWD/qv4enginebase_p.h \ $$PWD/qv4context_p.h \ $$PWD/qv4math_p.h \ $$PWD/qv4persistent_p.h \ diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index a57456c0fb..5cb0933e94 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -55,6 +55,7 @@ #include "qv4managed_p.h" #include "qv4context_p.h" #include +#include "qv4enginebase_p.h" #ifndef V4_BOOTSTRAP # include diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h new file mode 100644 index 0000000000..c86b8bb9a0 --- /dev/null +++ b/src/qml/jsruntime/qv4enginebase_p.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ENGINEBASE_P_H +#define QV4ENGINEBASE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +// Base class for the execution engine + +#if defined(Q_CC_MSVC) || defined(Q_CC_GNU) +#pragma pack(push, 1) +#endif +struct EngineBase { + Heap::ExecutionContext *current = 0; + + Value *jsStackTop = 0; + quint32 hasException = false; +#if QT_POINTER_SIZE == 8 + quint8 padding[4]; +#endif + MemoryManager *memoryManager = 0; + Runtime runtime; + + qint32 callDepth = 0; + Value *jsStackLimit = 0; + Value *jsStackBase = 0; + + ExecutionContext *currentContext = 0; + IdentifierTable *identifierTable = 0; + Object *globalObject = 0; +}; +#if defined(Q_CC_MSVC) || defined(Q_CC_GNU) +#pragma pack(pop) +#endif + +Q_STATIC_ASSERT(std::is_standard_layout::value); +Q_STATIC_ASSERT(offsetof(EngineBase, current) == 0); +Q_STATIC_ASSERT(offsetof(EngineBase, jsStackTop) == offsetof(EngineBase, current) + QT_POINTER_SIZE); +Q_STATIC_ASSERT(offsetof(EngineBase, hasException) == offsetof(EngineBase, jsStackTop) + QT_POINTER_SIZE); +Q_STATIC_ASSERT(offsetof(EngineBase, memoryManager) == offsetof(EngineBase, hasException) + QT_POINTER_SIZE); +Q_STATIC_ASSERT(offsetof(EngineBase, runtime) == offsetof(EngineBase, memoryManager) + QT_POINTER_SIZE); + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/memory/qv4mmdefs_p.h b/src/qml/memory/qv4mmdefs_p.h index 75cf4681d6..ef93971ab8 100644 --- a/src/qml/memory/qv4mmdefs_p.h +++ b/src/qml/memory/qv4mmdefs_p.h @@ -260,41 +260,6 @@ Q_STATIC_ASSERT(sizeof(HeapItem) == Chunk::SlotSize); Q_STATIC_ASSERT(QT_POINTER_SIZE*8 == Chunk::Bits); Q_STATIC_ASSERT((1 << Chunk::BitShift) == Chunk::Bits); -// Base class for the execution engine - -#if defined(Q_CC_MSVC) || defined(Q_CC_GNU) -#pragma pack(push, 1) -#endif -struct EngineBase { - Heap::ExecutionContext *current = 0; - - Value *jsStackTop = 0; - quint32 hasException = false; -#if QT_POINTER_SIZE == 8 - quint8 padding[4]; -#endif - MemoryManager *memoryManager = 0; - Runtime runtime; - - qint32 callDepth = 0; - Value *jsStackLimit = 0; - Value *jsStackBase = 0; - - ExecutionContext *currentContext = 0; - IdentifierTable *identifierTable = 0; - Object *globalObject = 0; -}; -#if defined(Q_CC_MSVC) || defined(Q_CC_GNU) -#pragma pack(pop) -#endif - -Q_STATIC_ASSERT(std::is_standard_layout::value); -Q_STATIC_ASSERT(offsetof(EngineBase, current) == 0); -Q_STATIC_ASSERT(offsetof(EngineBase, jsStackTop) == offsetof(EngineBase, current) + QT_POINTER_SIZE); -Q_STATIC_ASSERT(offsetof(EngineBase, hasException) == offsetof(EngineBase, jsStackTop) + QT_POINTER_SIZE); -Q_STATIC_ASSERT(offsetof(EngineBase, memoryManager) == offsetof(EngineBase, hasException) + QT_POINTER_SIZE); -Q_STATIC_ASSERT(offsetof(EngineBase, runtime) == offsetof(EngineBase, memoryManager) + QT_POINTER_SIZE); - } QT_END_NAMESPACE -- cgit v1.2.3 From 931239579d60eff13ef4f7674cc10f27d7bbdf71 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 12 May 2017 10:16:51 +0200 Subject: Move the list of default internal classes into EngineBase And store them in an enumerated array. This will simplify upcoming changes. Change-Id: I82eac03b9f6264843ae625e36e150464fe08be9d Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4compileddata.cpp | 2 +- src/qml/jsruntime/qv4engine.cpp | 90 +++++++++++++++++---------------- src/qml/jsruntime/qv4engine_p.h | 19 ------- src/qml/jsruntime/qv4enginebase_p.h | 19 +++++++ src/qml/jsruntime/qv4errorobject.cpp | 2 +- src/qml/jsruntime/qv4errorobject_p.h | 14 +++-- src/qml/jsruntime/qv4function.cpp | 4 +- src/qml/jsruntime/qv4functionobject.cpp | 6 +-- src/qml/jsruntime/qv4functionobject_p.h | 4 +- src/qml/jsruntime/qv4internalclass.cpp | 8 +-- src/qml/jsruntime/qv4object_p.h | 8 +-- src/qml/jsruntime/qv4regexpobject.cpp | 2 +- src/qml/jsruntime/qv4regexpobject_p.h | 2 +- src/qml/jsruntime/qv4runtime.cpp | 2 +- src/qml/jsruntime/qv4stringobject_p.h | 2 +- src/qml/jsruntime/qv4typedarray.cpp | 2 +- 16 files changed, 97 insertions(+), 89 deletions(-) (limited to 'src') diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index d59a72cde2..ac4192bc12 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -177,7 +177,7 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) for (uint i = 0; i < data->jsClassTableSize; ++i) { int memberCount = 0; const CompiledData::JSClassMember *member = data->jsClassAt(i, &memberCount); - QV4::InternalClass *klass = engine->emptyClass; + QV4::InternalClass *klass = engine->internalClasses[QV4::ExecutionEngine::Class_Object]; for (int j = 0; j < memberCount; ++j, ++member) klass = klass->addMember(runtimeStrings[member->nameOffset]->identifier, member->isAccessor ? QV4::Attr_Accessor : QV4::Attr_Data); diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index b72e1d9dcf..c7b87f209a 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -212,7 +212,9 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) classPool = new InternalClassPool; - emptyClass = new (classPool) InternalClass(this); + internalClasses[Class_Empty] = new (classPool) InternalClass(this); + internalClasses[Class_Object] = internalClasses[Class_Empty]; + jsStrings[String_Empty] = newIdentifier(QString()); jsStrings[String_undefined] = newIdentifier(QStringLiteral("undefined")); @@ -251,90 +253,90 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) jsStrings[String_buffer] = newIdentifier(QStringLiteral("buffer")); jsStrings[String_lastIndex] = newIdentifier(QStringLiteral("lastIndex")); - jsObjects[ObjectProto] = memoryManager->allocObject(emptyClass); + jsObjects[ObjectProto] = memoryManager->allocObject(internalClasses[Class_Object]); - arrayClass = emptyClass->addMember(id_length(), Attr_NotConfigurable|Attr_NotEnumerable); - jsObjects[ArrayProto] = memoryManager->allocObject(arrayClass, objectPrototype()); + internalClasses[Class_ArrayObject] = internalClasses[Class_Object]->addMember(id_length(), Attr_NotConfigurable|Attr_NotEnumerable); + jsObjects[ArrayProto] = memoryManager->allocObject(internalClasses[Class_ArrayObject], objectPrototype()); jsObjects[PropertyListProto] = memoryManager->allocObject(); - InternalClass *argsClass = emptyClass->addMember(id_length(), Attr_NotEnumerable); - argumentsObjectClass = argsClass->addMember(id_callee(), Attr_Data|Attr_NotEnumerable); - strictArgumentsObjectClass = argsClass->addMember(id_callee(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); - strictArgumentsObjectClass = strictArgumentsObjectClass->addMember(id_caller(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + InternalClass *argsClass = internalClasses[Class_Object]->addMember(id_length(), Attr_NotEnumerable); + internalClasses[EngineBase::Class_ArgumentsObject] = argsClass->addMember(id_callee(), Attr_Data|Attr_NotEnumerable); + argsClass = argsClass->addMember(id_callee(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + internalClasses[EngineBase::Class_StrictArgumentsObject] = argsClass->addMember(id_caller(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); *static_cast(globalObject) = newObject(); Q_ASSERT(globalObject->d()->vtable()); initRootContext(); - stringClass = emptyClass->addMember(id_length(), Attr_ReadOnly); - Q_ASSERT(stringClass->find(id_length()) == Heap::StringObject::LengthPropertyIndex); - jsObjects[StringProto] = memoryManager->allocObject(stringClass, objectPrototype()); - jsObjects[NumberProto] = memoryManager->allocObject(emptyClass, objectPrototype()); - jsObjects[BooleanProto] = memoryManager->allocObject(emptyClass, objectPrototype()); - jsObjects[DateProto] = memoryManager->allocObject(emptyClass, objectPrototype()); + internalClasses[EngineBase::Class_StringObject] = internalClasses[Class_Object]->addMember(id_length(), Attr_ReadOnly); + Q_ASSERT(internalClasses[EngineBase::Class_StringObject]->find(id_length()) == Heap::StringObject::LengthPropertyIndex); + jsObjects[StringProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_StringObject], objectPrototype()); + jsObjects[NumberProto] = memoryManager->allocObject(internalClasses[Class_Object], objectPrototype()); + jsObjects[BooleanProto] = memoryManager->allocObject(internalClasses[Class_Object], objectPrototype()); + jsObjects[DateProto] = memoryManager->allocObject(internalClasses[Class_Object], objectPrototype()); uint index; - InternalClass *functionProtoClass = emptyClass->addMember(id_prototype(), Attr_NotEnumerable, &index); + InternalClass *functionProtoClass = internalClasses[Class_Object]->addMember(id_prototype(), Attr_NotEnumerable, &index); Q_ASSERT(index == Heap::FunctionObject::Index_Prototype); jsObjects[FunctionProto] = memoryManager->allocObject(functionProtoClass, objectPrototype()); - functionClass = emptyClass->addMember(id_prototype(), Attr_NotEnumerable|Attr_NotConfigurable, &index); + internalClasses[EngineBase::Class_FunctionObject] = internalClasses[Class_Object]->addMember(id_prototype(), Attr_NotEnumerable|Attr_NotConfigurable, &index); Q_ASSERT(index == Heap::FunctionObject::Index_Prototype); - scriptFunctionClass = functionClass->addMember(id_name(), Attr_ReadOnly, &index); + internalClasses[EngineBase::Class_ScriptFunction] = internalClasses[EngineBase::Class_FunctionObject]->addMember(id_name(), Attr_ReadOnly, &index); Q_ASSERT(index == Heap::ScriptFunction::Index_Name); - scriptFunctionClass = scriptFunctionClass->addMember(id_length(), Attr_ReadOnly, &index); + internalClasses[EngineBase::Class_ScriptFunction] = internalClasses[EngineBase::Class_ScriptFunction]->addMember(id_length(), Attr_ReadOnly, &index); Q_ASSERT(index == Heap::ScriptFunction::Index_Length); - protoClass = emptyClass->addMember(id_constructor(), Attr_NotEnumerable, &index); + internalClasses[EngineBase::Class_ObjectProto] = internalClasses[Class_Object]->addMember(id_constructor(), Attr_NotEnumerable, &index); Q_ASSERT(index == Heap::FunctionObject::Index_ProtoConstructor); Scope scope(this); ScopedString str(scope); - regExpObjectClass = emptyClass->addMember(id_lastIndex(), Attr_NotEnumerable|Attr_NotConfigurable, &index); + internalClasses[EngineBase::Class_RegExpObject] = internalClasses[Class_Object]->addMember(id_lastIndex(), Attr_NotEnumerable|Attr_NotConfigurable, &index); Q_ASSERT(index == RegExpObject::Index_LastIndex); - regExpObjectClass = regExpObjectClass->addMember((str = newIdentifier(QStringLiteral("source"))), Attr_ReadOnly, &index); + internalClasses[EngineBase::Class_RegExpObject] = internalClasses[EngineBase::Class_RegExpObject]->addMember((str = newIdentifier(QStringLiteral("source"))), Attr_ReadOnly, &index); Q_ASSERT(index == RegExpObject::Index_Source); - regExpObjectClass = regExpObjectClass->addMember((str = newIdentifier(QStringLiteral("global"))), Attr_ReadOnly, &index); + internalClasses[EngineBase::Class_RegExpObject] = internalClasses[EngineBase::Class_RegExpObject]->addMember((str = newIdentifier(QStringLiteral("global"))), Attr_ReadOnly, &index); Q_ASSERT(index == RegExpObject::Index_Global); - regExpObjectClass = regExpObjectClass->addMember((str = newIdentifier(QStringLiteral("ignoreCase"))), Attr_ReadOnly, &index); + internalClasses[EngineBase::Class_RegExpObject] = internalClasses[EngineBase::Class_RegExpObject]->addMember((str = newIdentifier(QStringLiteral("ignoreCase"))), Attr_ReadOnly, &index); Q_ASSERT(index == RegExpObject::Index_IgnoreCase); - regExpObjectClass = regExpObjectClass->addMember((str = newIdentifier(QStringLiteral("multiline"))), Attr_ReadOnly, &index); + internalClasses[EngineBase::Class_RegExpObject] = internalClasses[EngineBase::Class_RegExpObject]->addMember((str = newIdentifier(QStringLiteral("multiline"))), Attr_ReadOnly, &index); Q_ASSERT(index == RegExpObject::Index_Multiline); - jsObjects[RegExpProto] = memoryManager->allocObject(regExpObjectClass, objectPrototype()); - regExpExecArrayClass = arrayClass->addMember(id_index(), Attr_Data, &index); + jsObjects[RegExpProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_RegExpObject], objectPrototype()); + internalClasses[EngineBase::Class_RegExpExecArray] = internalClasses[Class_ArrayObject]->addMember(id_index(), Attr_Data, &index); Q_ASSERT(index == RegExpObject::Index_ArrayIndex); - regExpExecArrayClass = regExpExecArrayClass->addMember(id_input(), Attr_Data, &index); + internalClasses[EngineBase::Class_RegExpExecArray] = internalClasses[EngineBase::Class_RegExpExecArray]->addMember(id_input(), Attr_Data, &index); Q_ASSERT(index == RegExpObject::Index_ArrayInput); - errorClass = emptyClass->addMember((str = newIdentifier(QStringLiteral("stack"))), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable, &index); + internalClasses[EngineBase::Class_ErrorObject] = internalClasses[Class_Object]->addMember((str = newIdentifier(QStringLiteral("stack"))), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorObject::Index_Stack); - errorClass = errorClass->addMember((str = newIdentifier(QStringLiteral("fileName"))), Attr_Data|Attr_NotEnumerable, &index); + internalClasses[EngineBase::Class_ErrorObject] = internalClasses[EngineBase::Class_ErrorObject]->addMember((str = newIdentifier(QStringLiteral("fileName"))), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorObject::Index_FileName); - errorClass = errorClass->addMember((str = newIdentifier(QStringLiteral("lineNumber"))), Attr_Data|Attr_NotEnumerable, &index); + internalClasses[EngineBase::Class_ErrorObject] = internalClasses[EngineBase::Class_ErrorObject]->addMember((str = newIdentifier(QStringLiteral("lineNumber"))), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorObject::Index_LineNumber); - errorClassWithMessage = errorClass->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index); + internalClasses[EngineBase::Class_ErrorObjectWithMessage] = internalClasses[EngineBase::Class_ErrorObject]->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorObject::Index_Message); - errorProtoClass = emptyClass->addMember(id_constructor(), Attr_Data|Attr_NotEnumerable, &index); + internalClasses[EngineBase::Class_ErrorProto] = internalClasses[Class_Object]->addMember(id_constructor(), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorPrototype::Index_Constructor); - errorProtoClass = errorProtoClass->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index); + internalClasses[EngineBase::Class_ErrorProto] = internalClasses[EngineBase::Class_ErrorProto]->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorPrototype::Index_Message); - errorProtoClass = errorProtoClass->addMember(id_name(), Attr_Data|Attr_NotEnumerable, &index); + internalClasses[EngineBase::Class_ErrorProto] = internalClasses[EngineBase::Class_ErrorProto]->addMember(id_name(), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorPrototype::Index_Name); jsObjects[GetStack_Function] = BuiltinFunction::create(rootContext(), str = newIdentifier(QStringLiteral("stack")), ErrorObject::method_get_stack); getStackFunction()->defineReadonlyProperty(id_length(), Primitive::fromInt32(0)); - jsObjects[ErrorProto] = memoryManager->allocObject(errorProtoClass, objectPrototype()); - jsObjects[EvalErrorProto] = memoryManager->allocObject(errorProtoClass, errorPrototype()); - jsObjects[RangeErrorProto] = memoryManager->allocObject(errorProtoClass, errorPrototype()); - jsObjects[ReferenceErrorProto] = memoryManager->allocObject(errorProtoClass, errorPrototype()); - jsObjects[SyntaxErrorProto] = memoryManager->allocObject(errorProtoClass, errorPrototype()); - jsObjects[TypeErrorProto] = memoryManager->allocObject(errorProtoClass, errorPrototype()); - jsObjects[URIErrorProto] = memoryManager->allocObject(errorProtoClass, errorPrototype()); + jsObjects[ErrorProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_ErrorProto], objectPrototype()); + jsObjects[EvalErrorProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_ErrorProto], errorPrototype()); + jsObjects[RangeErrorProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_ErrorProto], errorPrototype()); + jsObjects[ReferenceErrorProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_ErrorProto], errorPrototype()); + jsObjects[SyntaxErrorProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_ErrorProto], errorPrototype()); + jsObjects[TypeErrorProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_ErrorProto], errorPrototype()); + jsObjects[URIErrorProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_ErrorProto], errorPrototype()); - jsObjects[VariantProto] = memoryManager->allocObject(emptyClass, objectPrototype()); + jsObjects[VariantProto] = memoryManager->allocObject(internalClasses[Class_Object], objectPrototype()); Q_ASSERT(variantPrototype()->prototype() == objectPrototype()->d()); - jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject(arrayClass, arrayPrototype())); + jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject(internalClasses[Class_ArrayObject], arrayPrototype())); ExecutionContext *global = rootContext(); jsObjects[Object_Ctor] = memoryManager->allocObject(global); @@ -464,7 +466,7 @@ ExecutionEngine::~ExecutionEngine() for (QV4::CompiledData::CompilationUnit *unit : qAsConst(remainingUnits)) unit->unlink(); - emptyClass->destroy(); + internalClasses[Class_Empty]->destroy(); delete classPool; delete bumperPointerAllocator; delete regExpCache; diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 5cb0933e94..1d07196c28 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -233,25 +233,6 @@ public: Object *signalHandlerPrototype() const { return reinterpret_cast(jsObjects + SignalHandlerProto); } InternalClassPool *classPool; - InternalClass *emptyClass; - - InternalClass *arrayClass; - InternalClass *stringClass; - - InternalClass *functionClass; - InternalClass *scriptFunctionClass; - InternalClass *protoClass; - - InternalClass *regExpExecArrayClass; - InternalClass *regExpObjectClass; - - InternalClass *argumentsObjectClass; - InternalClass *strictArgumentsObjectClass; - - InternalClass *errorClass; - InternalClass *errorClassWithMessage; - InternalClass *errorProtoClass; - EvalFunction *evalFunction() const { return reinterpret_cast(jsObjects + Eval_Function); } FunctionObject *getStackFunction() const { return reinterpret_cast(jsObjects + GetStack_Function); } FunctionObject *thrower() const { return reinterpret_cast(jsObjects + ThrowerObject); } diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h index c86b8bb9a0..efcd7b16d9 100644 --- a/src/qml/jsruntime/qv4enginebase_p.h +++ b/src/qml/jsruntime/qv4enginebase_p.h @@ -80,6 +80,25 @@ struct EngineBase { ExecutionContext *currentContext = 0; IdentifierTable *identifierTable = 0; Object *globalObject = 0; + + enum { + Class_Empty, + Class_Object, + Class_ArrayObject, + Class_FunctionObject, + Class_StringObject, + Class_ScriptFunction, + Class_ObjectProto, + Class_RegExpExecArray, + Class_RegExpObject, + Class_ArgumentsObject, + Class_StrictArgumentsObject, + Class_ErrorObject, + Class_ErrorObjectWithMessage, + Class_ErrorProto, + NClasses + }; + InternalClass *internalClasses[NClasses]; }; #if defined(Q_CC_MSVC) || defined(Q_CC_GNU) #pragma pack(pop) diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp index f290bc5136..63b778b56d 100644 --- a/src/qml/jsruntime/qv4errorobject.cpp +++ b/src/qml/jsruntime/qv4errorobject.cpp @@ -75,7 +75,7 @@ void Heap::ErrorObject::init() Scope scope(internalClass->engine); Scoped e(scope, this); - if (internalClass == scope.engine->errorProtoClass) + if (internalClass == scope.engine->internalClasses[EngineBase::Class_ErrorProto]) return; *propertyData(QV4::ErrorObject::Index_Stack) = scope.engine->getStackFunction(); diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h index 9ba9f05234..896b54da5e 100644 --- a/src/qml/jsruntime/qv4errorobject_p.h +++ b/src/qml/jsruntime/qv4errorobject_p.h @@ -157,7 +157,7 @@ struct ErrorObject: Object { V4_OBJECT2(ErrorObject, Object) Q_MANAGED_TYPE(ErrorObject) - V4_INTERNALCLASS(errorClass) + V4_INTERNALCLASS(ErrorObject) V4_PROTOTYPE(errorPrototype) V4_NEEDS_DESTROY @@ -324,19 +324,25 @@ inline SyntaxErrorObject *ErrorObject::asSyntaxError() template Heap::Object *ErrorObject::create(ExecutionEngine *e, const Value &message) { - return e->memoryManager->allocObject(message.isUndefined() ? e->errorClass : e->errorClassWithMessage, T::defaultPrototype(e), message); + return e->memoryManager->allocObject( + e->internalClasses[message.isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage], + T::defaultPrototype(e), message); } template Heap::Object *ErrorObject::create(ExecutionEngine *e, const QString &message) { Scope scope(e); ScopedValue v(scope, message.isEmpty() ? Encode::undefined() : e->newString(message)->asReturnedValue()); - return e->memoryManager->allocObject(v->isUndefined() ? e->errorClass : e->errorClassWithMessage, T::defaultPrototype(e), v); + return e->memoryManager->allocObject( + e->internalClasses[v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage], + T::defaultPrototype(e), v); } template Heap::Object *ErrorObject::create(ExecutionEngine *e, const QString &message, const QString &filename, int line, int column) { Scope scope(e); ScopedValue v(scope, message.isEmpty() ? Encode::undefined() : e->newString(message)->asReturnedValue()); - return e->memoryManager->allocObject(v->isUndefined() ? e->errorClass : e->errorClassWithMessage, T::defaultPrototype(e), v, filename, line, column); + return e->memoryManager->allocObject( + e->internalClasses[v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage], + T::defaultPrototype(e), v, filename, line, column); } diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index 358c2d079c..994dede26d 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -59,7 +59,7 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, { Q_UNUSED(engine); - internalClass = engine->emptyClass; + internalClass = engine->internalClasses[EngineBase::Class_Empty]; const CompiledData::LEUInt32 *formalsIndices = compiledFunction->formalsTable(); // iterate backwards, so we get the right ordering for duplicate names Scope scope(engine); @@ -95,7 +95,7 @@ Function::~Function() void Function::updateInternalClass(ExecutionEngine *engine, const QList ¶meters) { - internalClass = engine->emptyClass; + internalClass = engine->internalClasses[EngineBase::Class_Empty]; // iterate backwards, so we get the right ordering for duplicate names Scope scope(engine); diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index b2d89220ea..86362fa0cb 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -122,8 +122,8 @@ void FunctionObject::init(String *n, bool createProto) Q_ASSERT(internalClass() && internalClass()->find(s.engine->id_prototype()) == Heap::FunctionObject::Index_Prototype); if (createProto) { - ScopedObject proto(s, scope()->engine->newObject(s.engine->protoClass, s.engine->objectPrototype())); - Q_ASSERT(s.engine->protoClass->find(s.engine->id_constructor()) == Heap::FunctionObject::Index_ProtoConstructor); + ScopedObject proto(s, scope()->engine->newObject(s.engine->internalClasses[EngineBase::Class_ObjectProto], s.engine->objectPrototype())); + Q_ASSERT(s.engine->internalClasses[EngineBase::Class_ObjectProto]->find(s.engine->id_constructor()) == Heap::FunctionObject::Index_ProtoConstructor); *proto->propertyData(Heap::FunctionObject::Index_ProtoConstructor) = this->asReturnedValue(); *propertyData(Heap::FunctionObject::Index_Prototype) = proto.asReturnedValue(); } else { @@ -375,7 +375,7 @@ void ScriptFunction::construct(const Managed *that, Scope &scope, CallData *call Scoped f(scope, static_cast(that)); - InternalClass *ic = v4->emptyClass; + InternalClass *ic = v4->internalClasses[EngineBase::Class_Object]; ScopedObject proto(scope, f->protoForConstructor()); ScopedObject obj(scope, v4->newObject(ic, proto)); callData->thisObject = obj.asReturnedValue(); diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index f4ac37219c..bfaa1ae056 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -134,7 +134,7 @@ struct Q_QML_EXPORT FunctionObject: Object { }; V4_OBJECT2(FunctionObject, Object) Q_MANAGED_TYPE(FunctionObject) - V4_INTERNALCLASS(functionClass) + V4_INTERNALCLASS(FunctionObject) V4_PROTOTYPE(functionPrototype) V4_NEEDS_DESTROY @@ -236,7 +236,7 @@ void Heap::IndexedBuiltinFunction::init(QV4::ExecutionContext *scope, uint index struct ScriptFunction : FunctionObject { V4_OBJECT2(ScriptFunction, FunctionObject) - V4_INTERNALCLASS(scriptFunctionClass) + V4_INTERNALCLASS(ScriptFunction) static void construct(const Managed *, Scope &scope, CallData *callData); static void call(const Managed *that, Scope &scope, CallData *callData); diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index 7e3fd7dc12..162de0b9f7 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -220,7 +220,7 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri return t.lookup; // create a new class and add it to the tree - InternalClass *newClass = engine->emptyClass; + InternalClass *newClass = engine->internalClasses[EngineBase::Class_Empty]; for (uint i = 0; i < size; ++i) { if (i == idx) { newClass = newClass->addMember(nameMap.at(i), data); @@ -332,7 +332,7 @@ void InternalClass::removeMember(Object *object, Identifier *id) object->setInternalClass(t.lookup); } else { // create a new class and add it to the tree - InternalClass *newClass = oldClass->engine->emptyClass; + InternalClass *newClass = oldClass->engine->internalClasses[EngineBase::Class_Empty]; for (uint i = 0; i < oldClass->size; ++i) { if (i == propIdx) continue; @@ -365,7 +365,7 @@ InternalClass *InternalClass::sealed() if (m_sealed) return m_sealed; - m_sealed = engine->emptyClass; + m_sealed = engine->internalClasses[EngineBase::Class_Empty]; for (uint i = 0; i < size; ++i) { PropertyAttributes attrs = propertyData.at(i); if (attrs.isEmpty()) @@ -394,7 +394,7 @@ InternalClass *InternalClass::frozen() InternalClass *InternalClass::propertiesFrozen() const { - InternalClass *frozen = engine->emptyClass; + InternalClass *frozen = engine->internalClasses[EngineBase::Class_Empty]; for (uint i = 0; i < size; ++i) { PropertyAttributes attrs = propertyData.at(i); if (attrs.isEmpty()) diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 45392c0486..80bfbe941a 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -138,8 +138,8 @@ struct Object : Base { V4_ASSERT_IS_TRIVIAL(QV4::Heap::DataClass); #define V4_INTERNALCLASS(c) \ - static QV4::InternalClass *defaultInternalClass(QV4::ExecutionEngine *e) \ - { return e->c; } + static QV4::InternalClass *defaultInternalClass(QV4::EngineBase *e) \ +{ return e->internalClasses[QV4::EngineBase::Class_##c]; } #define V4_PROTOTYPE(p) \ static QV4::Object *defaultPrototype(QV4::ExecutionEngine *e) \ { return e->p(); } @@ -198,7 +198,7 @@ QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF struct Q_QML_EXPORT Object: Managed { V4_OBJECT2(Object, Object) Q_MANAGED_TYPE(Object) - V4_INTERNALCLASS(emptyClass) + V4_INTERNALCLASS(Object) V4_PROTOTYPE(objectPrototype) enum { @@ -472,7 +472,7 @@ struct NumberObject: Object { struct ArrayObject: Object { V4_OBJECT2(ArrayObject, Object) Q_MANAGED_TYPE(ArrayObject) - V4_INTERNALCLASS(arrayClass) + V4_INTERNALCLASS(ArrayObject) V4_PROTOTYPE(arrayPrototype) void init(ExecutionEngine *engine); diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 0894d0c25b..1f758e36f6 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -379,7 +379,7 @@ void RegExpPrototype::method_exec(const BuiltinFunction *, Scope &scope, CallDat } // fill in result data - ScopedArrayObject array(scope, scope.engine->newArrayObject(scope.engine->regExpExecArrayClass, scope.engine->arrayPrototype())); + ScopedArrayObject array(scope, scope.engine->newArrayObject(scope.engine->internalClasses[EngineBase::Class_RegExpExecArray], scope.engine->arrayPrototype())); int len = r->value()->captureCount(); array->arrayReserve(len); ScopedValue v(scope); diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h index c0c7dfa78a..54731cef14 100644 --- a/src/qml/jsruntime/qv4regexpobject_p.h +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -96,7 +96,7 @@ struct RegExpCtor : FunctionObject { struct RegExpObject: Object { V4_OBJECT2(RegExpObject, Object) Q_MANAGED_TYPE(RegExpObject) - V4_INTERNALCLASS(regExpObjectClass) + V4_INTERNALCLASS(RegExpObject) V4_PROTOTYPE(regExpPrototype) // needs to be compatible with the flags in qv4jsir_p.h diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 4b440f5335..a79eab3778 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -1408,7 +1408,7 @@ QV4::ReturnedValue Runtime::method_setupArgumentsObject(ExecutionEngine *engine) { Q_ASSERT(engine->current->type == Heap::ExecutionContext::Type_CallContext); QV4::CallContext *c = static_cast(engine->currentContext); - QV4::InternalClass *ic = c->d()->strictMode ? engine->strictArgumentsObjectClass : engine->argumentsObjectClass; + QV4::InternalClass *ic = engine->internalClasses[c->d()->strictMode ? EngineBase::Class_StrictArgumentsObject : EngineBase::Class_ArgumentsObject]; return engine->memoryManager->allocObject(ic, engine->objectPrototype(), c)->asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h index 0ee7a6ece9..b8fb80546f 100644 --- a/src/qml/jsruntime/qv4stringobject_p.h +++ b/src/qml/jsruntime/qv4stringobject_p.h @@ -82,7 +82,7 @@ struct StringCtor : FunctionObject { struct StringObject: Object { V4_OBJECT2(StringObject, Object) Q_MANAGED_TYPE(StringObject) - V4_INTERNALCLASS(stringClass) + V4_INTERNALCLASS(StringObject) V4_PROTOTYPE(stringPrototype) Heap::String *getIndex(uint index) const { diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index cecd1e6958..542460ee81 100644 --- a/src/qml/jsruntime/qv4typedarray.cpp +++ b/src/qml/jsruntime/qv4typedarray.cpp @@ -372,7 +372,7 @@ void Heap::TypedArray::init(Type t) Heap::TypedArray *TypedArray::create(ExecutionEngine *e, Heap::TypedArray::Type t) { - return e->memoryManager->allocObject(e->emptyClass, e->typedArrayPrototype + t, t); + return e->memoryManager->allocObject(e->internalClasses[EngineBase::Class_Object], e->typedArrayPrototype + t, t); } void TypedArray::markObjects(Heap::Base *that, ExecutionEngine *e) -- cgit v1.2.3 From 70a49fe042dd244926cc4a9cb6affb8b4f3d9b7f Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 12 May 2017 10:29:23 +0200 Subject: Add ICs for String, MemberData and ArrayData Change-Id: I43ddcb4842e501cbea8a950ab6ffa2d906014efd Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4arraydata_p.h | 2 ++ src/qml/jsruntime/qv4engine.cpp | 4 ++++ src/qml/jsruntime/qv4enginebase_p.h | 4 ++++ src/qml/jsruntime/qv4managed_p.h | 5 +++++ src/qml/jsruntime/qv4memberdata_p.h | 1 + src/qml/jsruntime/qv4object_p.h | 3 --- src/qml/jsruntime/qv4string_p.h | 2 ++ 7 files changed, 18 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/qml/jsruntime/qv4arraydata_p.h b/src/qml/jsruntime/qv4arraydata_p.h index 24b948f01e..daf8c36814 100644 --- a/src/qml/jsruntime/qv4arraydata_p.h +++ b/src/qml/jsruntime/qv4arraydata_p.h @@ -236,6 +236,7 @@ struct Q_QML_EXPORT ArrayData : public Managed struct Q_QML_EXPORT SimpleArrayData : public ArrayData { V4_ARRAYDATA(SimpleArrayData) + V4_INTERNALCLASS(SimpleArrayData) uint mappedIndex(uint index) const { return d()->mappedIndex(index); } Value data(uint index) const { return d()->data(index); } @@ -262,6 +263,7 @@ struct Q_QML_EXPORT SimpleArrayData : public ArrayData struct Q_QML_EXPORT SparseArrayData : public ArrayData { V4_ARRAYDATA(SparseArrayData) + V4_INTERNALCLASS(SparseArrayData) V4_NEEDS_DESTROY ReturnedValue &freeList() { return d()->freeList; } diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index c7b87f209a..982db33092 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -215,6 +215,10 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) internalClasses[Class_Empty] = new (classPool) InternalClass(this); internalClasses[Class_Object] = internalClasses[Class_Empty]; + internalClasses[Class_String] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::String::staticVTable()); + internalClasses[Class_MemberData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::MemberData::staticVTable()); + internalClasses[Class_SimpleArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SimpleArrayData::staticVTable()); + internalClasses[Class_SparseArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SparseArrayData::staticVTable()); jsStrings[String_Empty] = newIdentifier(QString()); jsStrings[String_undefined] = newIdentifier(QStringLiteral("undefined")); diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h index efcd7b16d9..783ddc5bd3 100644 --- a/src/qml/jsruntime/qv4enginebase_p.h +++ b/src/qml/jsruntime/qv4enginebase_p.h @@ -83,6 +83,10 @@ struct EngineBase { enum { Class_Empty, + Class_String, + Class_MemberData, + Class_SimpleArrayData, + Class_SparseArrayData, Class_Object, Class_ArrayObject, Class_FunctionObject, diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h index 7e674c6ec7..00bfad78dd 100644 --- a/src/qml/jsruntime/qv4managed_p.h +++ b/src/qml/jsruntime/qv4managed_p.h @@ -52,6 +52,7 @@ #include "qv4global_p.h" #include "qv4value_p.h" +#include "qv4enginebase_p.h" #include QT_BEGIN_NAMESPACE @@ -151,6 +152,10 @@ QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON \ const QV4::VTable classname::static_vtbl = DEFINE_MANAGED_VTABLE_INT(classname, 0) \ QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF +#define V4_INTERNALCLASS(c) \ + static QV4::InternalClass *defaultInternalClass(QV4::EngineBase *e) \ + { return e->internalClasses[QV4::EngineBase::Class_##c]; } + struct Q_QML_PRIVATE_EXPORT Managed : Value { V4_MANAGED_ITSELF(Base, Managed) diff --git a/src/qml/jsruntime/qv4memberdata_p.h b/src/qml/jsruntime/qv4memberdata_p.h index 5c89dfe8ec..e239458849 100644 --- a/src/qml/jsruntime/qv4memberdata_p.h +++ b/src/qml/jsruntime/qv4memberdata_p.h @@ -73,6 +73,7 @@ V4_ASSERT_IS_TRIVIAL(MemberData) struct MemberData : Managed { V4_MANAGED(MemberData, Managed) + V4_INTERNALCLASS(MemberData) Value &operator[] (uint idx) { return d()->data[idx]; } const Value *data() const { return d()->data; } diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 80bfbe941a..e1b2a40b94 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -137,9 +137,6 @@ struct Object : Base { } \ V4_ASSERT_IS_TRIVIAL(QV4::Heap::DataClass); -#define V4_INTERNALCLASS(c) \ - static QV4::InternalClass *defaultInternalClass(QV4::EngineBase *e) \ -{ return e->internalClasses[QV4::EngineBase::Class_##c]; } #define V4_PROTOTYPE(p) \ static QV4::Object *defaultPrototype(QV4::ExecutionEngine *e) \ { return e->p(); } diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index 5b0fd292d6..bf8c48ceee 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -53,6 +53,7 @@ #include #include "qv4managed_p.h" #include +#include "qv4enginebase_p.h" QT_BEGIN_NAMESPACE @@ -71,6 +72,7 @@ struct Q_QML_PRIVATE_EXPORT String : Base { }; #ifndef V4_BOOTSTRAP + V4_INTERNALCLASS(String) void init(MemoryManager *mm, const QString &text); void init(MemoryManager *mm, String *l, String *n); void destroy(); -- cgit v1.2.3 From cdbc4b83d59e08189d6ece9ccd88a646be155c08 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 12 May 2017 15:01:07 +0200 Subject: Properly encapsulate all accesses to the vtable Change-Id: I3f6ae59d01c7b6c898e98d3b6f65b84a19b8851a Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4internalclass.cpp | 4 ++-- src/qml/jsruntime/qv4lookup.cpp | 4 ++-- src/qml/jsruntime/qv4object.cpp | 6 +++--- src/qml/jsruntime/qv4object_p.h | 16 ++++++++-------- 4 files changed, 15 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index 162de0b9f7..f310b6f551 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -129,7 +129,7 @@ InternalClass::InternalClass(const QV4::InternalClass &other) static void insertHoleIntoPropertyData(Object *object, int idx) { - int inlineSize = object->d()->vt->nInlineProperties; + int inlineSize = object->d()->vtable()->nInlineProperties; int icSize = object->internalClass()->size; int from = qMax(idx, inlineSize); int to = from + 1; @@ -151,7 +151,7 @@ static void insertHoleIntoPropertyData(Object *object, int idx) static void removeFromPropertyData(Object *object, int idx, bool accessor = false) { - int inlineSize = object->d()->vt->nInlineProperties; + int inlineSize = object->d()->vtable()->nInlineProperties; int delta = (accessor ? 2 : 1); int oldSize = object->internalClass()->size + delta; int to = idx; diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index 0d467098fe..f8ac0cb650 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -287,7 +287,7 @@ ReturnedValue Lookup::getterGeneric(Lookup *l, ExecutionEngine *engine, const Va l->proto = proto->d(); if (attrs.isData()) { if (l->level == 0) { - uint nInline = l->proto->vt->nInlineProperties; + uint nInline = l->proto->vtable()->nInlineProperties; if (l->index < nInline) l->getter = Lookup::primitiveGetter0Inline; else { @@ -696,7 +696,7 @@ ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionEngine *engine) if (v != Primitive::emptyValue().asReturnedValue()) { if (attrs.isData()) { if (l->level == 0) { - uint nInline = o->d()->vt->nInlineProperties; + uint nInline = o->d()->vtable()->nInlineProperties; if (l->index < nInline) l->globalGetter = globalGetter0Inline; else { diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 3d8e8f3ddf..f5dafa7914 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -266,7 +266,7 @@ void Object::markObjects(Heap::Base *that, ExecutionEngine *e) if (o->prototype) o->prototype->mark(e); uint nInline = o->vtable()->nInlineProperties; - Value *v = reinterpret_cast(o) + o->vt->inlinePropertyOffset; + Value *v = reinterpret_cast(o) + o->vtable()->inlinePropertyOffset; const Value *end = v + nInline; while (v < end) { v->mark(e); @@ -507,7 +507,7 @@ ReturnedValue Object::getLookup(const Managed *m, Lookup *l) if (v != Primitive::emptyValue().asReturnedValue()) { if (attrs.isData()) { if (l->level == 0) { - uint nInline = o->d()->vt->nInlineProperties; + uint nInline = o->d()->vtable()->nInlineProperties; if (l->index < nInline) l->getter = Lookup::getter0Inline; else { @@ -549,7 +549,7 @@ void Object::setLookup(Managed *m, Lookup *l, const Value &value) if (idx != UINT_MAX && o->internalClass()->propertyData[idx].isData() && o->internalClass()->propertyData[idx].isWritable()) { l->classList[0] = o->internalClass(); l->index = idx; - l->setter = idx < o->d()->vt->nInlineProperties ? Lookup::setter0Inline : Lookup::setter0; + l->setter = idx < o->d()->vtable()->nInlineProperties ? Lookup::setter0Inline : Lookup::setter0; *o->propertyData(idx) = value; return; } diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index e1b2a40b94..78ee263c80 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -72,25 +72,25 @@ struct Object : Base { void destroy() { Base::destroy(); } const Value *inlinePropertyData(uint index) const { - Q_ASSERT(index < vt->nInlineProperties); - return reinterpret_cast(this) + vt->inlinePropertyOffset + index; + Q_ASSERT(index < vtable()->nInlineProperties); + return reinterpret_cast(this) + vtable()->inlinePropertyOffset + index; } Value *inlinePropertyData(uint index) { - Q_ASSERT(index < vt->nInlineProperties); - return reinterpret_cast(this) + vt->inlinePropertyOffset + index; + Q_ASSERT(index < vtable()->nInlineProperties); + return reinterpret_cast(this) + vtable()->inlinePropertyOffset + index; } const Value *propertyData(uint index) const { - uint nInline = vt->nInlineProperties; + uint nInline = vtable()->nInlineProperties; if (index < nInline) - return reinterpret_cast(this) + vt->inlinePropertyOffset + index; + return reinterpret_cast(this) + vtable()->inlinePropertyOffset + index; index -= nInline; return memberData->data + index; } Value *propertyData(uint index) { - uint nInline = vt->nInlineProperties; + uint nInline = vtable()->nInlineProperties; if (index < nInline) - return reinterpret_cast(this) + vt->inlinePropertyOffset + index; + return reinterpret_cast(this) + vtable()->inlinePropertyOffset + index; index -= nInline; return memberData->data + index; } -- cgit v1.2.3 From 0cdea188727e203ecc529ef8e4e8859cca0be232 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 12 May 2017 15:04:53 +0200 Subject: Add support for storing the Vtable in the InternalClass Prepare for moving the vtable pointer into the internalClass. This adds the required infrastructure to InternalClass, so it can store a vtable pointer and properly handles vtable changes. Change-Id: I688fee1647268dd185d0f9636ab5b3390465daca Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4internalclass.cpp | 52 ++++++++++++++++++++++++++++------ src/qml/jsruntime/qv4internalclass_p.h | 32 ++++++++++----------- 2 files changed, 59 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index f310b6f551..e117da1a04 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -105,6 +105,7 @@ void PropertyHash::addEntry(const PropertyHash::Entry &entry, int classSize) InternalClass::InternalClass(ExecutionEngine *engine) : engine(engine) + , vtable(0) , m_sealed(0) , m_frozen(0) , size(0) @@ -116,6 +117,7 @@ InternalClass::InternalClass(ExecutionEngine *engine) InternalClass::InternalClass(const QV4::InternalClass &other) : QQmlJS::Managed() , engine(other.engine) + , vtable(other.vtable) , propertyTable(other.propertyTable) , nameMap(other.nameMap) , propertyData(other.propertyData) @@ -214,13 +216,13 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri if (data == propertyData.at(idx)) return this; - Transition temp = { identifier, 0, (int)data.flags() }; + Transition temp = { { identifier }, nullptr, (int)data.flags() }; Transition &t = lookupOrInsertTransition(temp); if (t.lookup) return t.lookup; // create a new class and add it to the tree - InternalClass *newClass = engine->internalClasses[EngineBase::Class_Empty]; + InternalClass *newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable); for (uint i = 0; i < size; ++i) { if (i == idx) { newClass = newClass->addMember(nameMap.at(i), data); @@ -234,12 +236,34 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri return newClass; } +InternalClass *InternalClass::changeVTableImpl(const VTable *vt) +{ + Q_ASSERT(vtable != vt); + + Transition temp = { { nullptr }, nullptr, Transition::VTableChange }; + temp.vtable = vt; + + Transition &t = lookupOrInsertTransition(temp); + if (t.lookup) + return t.lookup; + + // create a new class and add it to the tree + InternalClass *newClass; + newClass = engine->newClass(*this); + newClass->vtable = vt; + + t.lookup = newClass; + Q_ASSERT(t.lookup); + Q_ASSERT(newClass->vtable); + return newClass; +} + InternalClass *InternalClass::nonExtensible() { if (!extensible) return this; - Transition temp = { Q_NULLPTR, Q_NULLPTR, Transition::NotExtensible}; + Transition temp = { { nullptr }, nullptr, Transition::NotExtensible}; Transition &t = lookupOrInsertTransition(temp); if (t.lookup) return t.lookup; @@ -287,7 +311,7 @@ InternalClass *InternalClass::addMember(Identifier *identifier, PropertyAttribut InternalClass *InternalClass::addMemberImpl(Identifier *identifier, PropertyAttributes data, uint *index) { - Transition temp = { identifier, 0, (int)data.flags() }; + Transition temp = { { identifier }, nullptr, (int)data.flags() }; Transition &t = lookupOrInsertTransition(temp); if (index) @@ -323,7 +347,7 @@ void InternalClass::removeMember(Object *object, Identifier *id) uint propIdx = oldClass->propertyTable.lookup(id); Q_ASSERT(propIdx < oldClass->size); - Transition temp = { id, 0, -1 }; + Transition temp = { { id }, nullptr, -1 }; Transition &t = object->internalClass()->lookupOrInsertTransition(temp); bool accessor = oldClass->propertyData.at(propIdx).isAccessor(); @@ -332,7 +356,7 @@ void InternalClass::removeMember(Object *object, Identifier *id) object->setInternalClass(t.lookup); } else { // create a new class and add it to the tree - InternalClass *newClass = oldClass->engine->internalClasses[EngineBase::Class_Empty]; + InternalClass *newClass = oldClass->engine->internalClasses[EngineBase::Class_Empty]->changeVTable(oldClass->vtable); for (uint i = 0; i < oldClass->size; ++i) { if (i == propIdx) continue; @@ -351,6 +375,18 @@ void InternalClass::removeMember(Object *object, Identifier *id) Q_ASSERT(t.lookup); } +uint QV4::InternalClass::find(const String *string) +{ + engine->identifierTable->identifier(string); + const Identifier *id = string->d()->identifier; + + uint index = propertyTable.lookup(id); + if (index < size) + return index; + + return UINT_MAX; +} + uint InternalClass::find(const Identifier *id) { uint index = propertyTable.lookup(id); @@ -365,7 +401,7 @@ InternalClass *InternalClass::sealed() if (m_sealed) return m_sealed; - m_sealed = engine->internalClasses[EngineBase::Class_Empty]; + m_sealed = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable); for (uint i = 0; i < size; ++i) { PropertyAttributes attrs = propertyData.at(i); if (attrs.isEmpty()) @@ -394,7 +430,7 @@ InternalClass *InternalClass::frozen() InternalClass *InternalClass::propertiesFrozen() const { - InternalClass *frozen = engine->internalClasses[EngineBase::Class_Empty]; + InternalClass *frozen = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable); for (uint i = 0; i < size; ++i) { PropertyAttributes attrs = propertyData.at(i); if (attrs.isEmpty()) diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index 1d8ef4b0fb..c68a6638e7 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -54,15 +54,13 @@ #include #include -#include -#include +#include QT_BEGIN_NAMESPACE namespace QV4 { struct String; -struct ExecutionEngine; struct Object; struct Identifier; struct VTable; @@ -222,12 +220,16 @@ private: struct InternalClassTransition { - Identifier *id; + union { + Identifier *id; + const VTable *vtable; + }; InternalClass *lookup; int flags; enum { // range 0-0xff is reserved for attribute changes - NotExtensible = 0x100 + NotExtensible = 0x100, + VTableChange = 0x200, }; bool operator==(const InternalClassTransition &other) const @@ -239,6 +241,7 @@ struct InternalClassTransition struct InternalClass : public QQmlJS::Managed { ExecutionEngine *engine; + const VTable *vtable; PropertyHash propertyTable; // id to valueIndex SharedInternalClassData nameMap; @@ -255,6 +258,12 @@ struct InternalClass : public QQmlJS::Managed { bool extensible; InternalClass *nonExtensible(); + InternalClass *changeVTable(const VTable *vt) { + if (vtable == vt) + return this; + return changeVTableImpl(vt); + } + static void addMember(Object *object, String *string, PropertyAttributes data, uint *index); InternalClass *addMember(String *string, PropertyAttributes data, uint *index = 0); InternalClass *addMember(Identifier *identifier, PropertyAttributes data, uint *index = 0); @@ -271,24 +280,13 @@ struct InternalClass : public QQmlJS::Managed { void destroy(); private: + InternalClass *changeVTableImpl(const VTable *vt); InternalClass *addMemberImpl(Identifier *identifier, PropertyAttributes data, uint *index); friend struct ExecutionEngine; InternalClass(ExecutionEngine *engine); InternalClass(const InternalClass &other); }; -inline uint InternalClass::find(const String *string) -{ - engine->identifierTable->identifier(string); - const Identifier *id = string->d()->identifier; - - uint index = propertyTable.lookup(id); - if (index < size) - return index; - - return UINT_MAX; -} - struct InternalClassPool : public QQmlJS::MemoryPool { void markObjects(ExecutionEngine *engine); -- cgit v1.2.3 From cae7975a036352ca4bbcf1381a445362f8e01367 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 12 May 2017 15:12:45 +0200 Subject: Move the internalClass field from Heap::Object to Heap::Base And do not store the vtable in Heap::Base anymore. This change makes the internal class the main distinguishing feature of all garbage collected objects. It also saves one pointer on all Objects. No measurable impact on runtime performance. Change-Id: I040a28b7581b993f1886b5219e279173dfa567e8 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4booleanobject_p.h | 1 + src/qml/jsruntime/qv4context_p.h | 1 + src/qml/jsruntime/qv4dateobject_p.h | 2 ++ src/qml/jsruntime/qv4engine.cpp | 40 +++++++++++++++++++++------------ src/qml/jsruntime/qv4enginebase_p.h | 4 +++- src/qml/jsruntime/qv4errorobject.cpp | 2 -- src/qml/jsruntime/qv4errorobject_p.h | 4 +++- src/qml/jsruntime/qv4internalclass.cpp | 13 +++++++++-- src/qml/jsruntime/qv4internalclass_p.h | 2 +- src/qml/jsruntime/qv4managed_p.h | 2 ++ src/qml/jsruntime/qv4numberobject_p.h | 1 + src/qml/jsruntime/qv4object.cpp | 1 + src/qml/jsruntime/qv4object_p.h | 2 -- src/qml/jsruntime/qv4qmlcontext.cpp | 2 ++ src/qml/jsruntime/qv4qobjectwrapper.cpp | 1 - src/qml/jsruntime/qv4regexp_p.h | 1 + src/qml/jsruntime/qv4sequenceobject_p.h | 1 + src/qml/jsruntime/qv4string_p.h | 2 +- src/qml/jsruntime/qv4typedarray.cpp | 3 ++- src/qml/jsruntime/qv4typedarray_p.h | 1 + src/qml/jsruntime/qv4variantobject_p.h | 1 + src/qml/memory/qv4heap_p.h | 10 +++++---- src/qml/memory/qv4mm.cpp | 3 ++- src/qml/memory/qv4mm_p.h | 17 +++++++++----- 24 files changed, 81 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/src/qml/jsruntime/qv4booleanobject_p.h b/src/qml/jsruntime/qv4booleanobject_p.h index 9c8b1d67f1..ca2cf7d17a 100644 --- a/src/qml/jsruntime/qv4booleanobject_p.h +++ b/src/qml/jsruntime/qv4booleanobject_p.h @@ -76,6 +76,7 @@ struct BooleanCtor: FunctionObject struct BooleanPrototype: BooleanObject { + V4_PROTOTYPE(objectPrototype) void init(ExecutionEngine *engine, Object *ctor); static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index c769dcd142..c742df7b97 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -228,6 +228,7 @@ struct Q_QML_EXPORT ExecutionContext : public Managed V4_MANAGED(ExecutionContext, Managed) Q_MANAGED_TYPE(ExecutionContext) + V4_INTERNALCLASS(ExecutionContext) ExecutionEngine *engine() const { return d()->engine; } diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index a56d17f9b1..ecd57bcd8d 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -114,6 +114,8 @@ struct DateCtor: FunctionObject struct DatePrototype: DateObject { + V4_PROTOTYPE(objectPrototype) + void init(ExecutionEngine *engine, Object *ctor); static double getThisDate(Scope &scope, CallData *callData); diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 982db33092..6b05d9a05c 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -213,12 +213,12 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) classPool = new InternalClassPool; internalClasses[Class_Empty] = new (classPool) InternalClass(this); - internalClasses[Class_Object] = internalClasses[Class_Empty]; - + internalClasses[Class_Object] = internalClasses[Class_Empty]->changeVTable(QV4::Object::staticVTable()); internalClasses[Class_String] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::String::staticVTable()); internalClasses[Class_MemberData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::MemberData::staticVTable()); internalClasses[Class_SimpleArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SimpleArrayData::staticVTable()); internalClasses[Class_SparseArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SparseArrayData::staticVTable()); + internalClasses[Class_ExecutionContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::ExecutionContext::staticVTable()); jsStrings[String_Empty] = newIdentifier(QString()); jsStrings[String_undefined] = newIdentifier(QStringLiteral("undefined")); @@ -259,11 +259,13 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) jsObjects[ObjectProto] = memoryManager->allocObject(internalClasses[Class_Object]); - internalClasses[Class_ArrayObject] = internalClasses[Class_Object]->addMember(id_length(), Attr_NotConfigurable|Attr_NotEnumerable); + internalClasses[Class_ArrayObject] = internalClasses[Class_Empty]->changeVTable(QV4::ArrayObject::staticVTable()); + internalClasses[Class_ArrayObject] = internalClasses[Class_ArrayObject]->addMember(id_length(), Attr_NotConfigurable|Attr_NotEnumerable); jsObjects[ArrayProto] = memoryManager->allocObject(internalClasses[Class_ArrayObject], objectPrototype()); jsObjects[PropertyListProto] = memoryManager->allocObject(); - InternalClass *argsClass = internalClasses[Class_Object]->addMember(id_length(), Attr_NotEnumerable); + InternalClass *argsClass = internalClasses[Class_Empty]->changeVTable(QV4::ArgumentsObject::staticVTable()) + ->addMember(id_length(), Attr_NotEnumerable); internalClasses[EngineBase::Class_ArgumentsObject] = argsClass->addMember(id_callee(), Attr_Data|Attr_NotEnumerable); argsClass = argsClass->addMember(id_callee(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); internalClasses[EngineBase::Class_StrictArgumentsObject] = argsClass->addMember(id_caller(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); @@ -272,15 +274,18 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) Q_ASSERT(globalObject->d()->vtable()); initRootContext(); - internalClasses[EngineBase::Class_StringObject] = internalClasses[Class_Object]->addMember(id_length(), Attr_ReadOnly); + internalClasses[Class_StringObject] = internalClasses[Class_Empty]->changeVTable(QV4::StringObject::staticVTable()); + internalClasses[EngineBase::Class_StringObject] = internalClasses[Class_StringObject]->addMember(id_length(), Attr_ReadOnly); Q_ASSERT(internalClasses[EngineBase::Class_StringObject]->find(id_length()) == Heap::StringObject::LengthPropertyIndex); jsObjects[StringProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_StringObject], objectPrototype()); - jsObjects[NumberProto] = memoryManager->allocObject(internalClasses[Class_Object], objectPrototype()); - jsObjects[BooleanProto] = memoryManager->allocObject(internalClasses[Class_Object], objectPrototype()); - jsObjects[DateProto] = memoryManager->allocObject(internalClasses[Class_Object], objectPrototype()); + jsObjects[NumberProto] = memoryManager->allocObject(); + jsObjects[BooleanProto] = memoryManager->allocObject(); + jsObjects[DateProto] = memoryManager->allocObject(); uint index; - InternalClass *functionProtoClass = internalClasses[Class_Object]->addMember(id_prototype(), Attr_NotEnumerable, &index); + InternalClass *functionProtoClass = + internalClasses[Class_Empty]->changeVTable(QV4::FunctionPrototype::staticVTable()) + ->addMember(id_prototype(), Attr_NotEnumerable, &index); Q_ASSERT(index == Heap::FunctionObject::Index_Prototype); jsObjects[FunctionProto] = memoryManager->allocObject(functionProtoClass, objectPrototype()); internalClasses[EngineBase::Class_FunctionObject] = internalClasses[Class_Object]->addMember(id_prototype(), Attr_NotEnumerable|Attr_NotConfigurable, &index); @@ -294,7 +299,10 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) Scope scope(this); ScopedString str(scope); - internalClasses[EngineBase::Class_RegExpObject] = internalClasses[Class_Object]->addMember(id_lastIndex(), Attr_NotEnumerable|Attr_NotConfigurable, &index); + internalClasses[Class_RegExp] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::RegExp::staticVTable()); + internalClasses[EngineBase::Class_RegExpObject] = + internalClasses[Class_Empty]->changeVTable(QV4::RegExpObject::staticVTable()) + ->addMember(id_lastIndex(), Attr_NotEnumerable|Attr_NotConfigurable, &index); Q_ASSERT(index == RegExpObject::Index_LastIndex); internalClasses[EngineBase::Class_RegExpObject] = internalClasses[EngineBase::Class_RegExpObject]->addMember((str = newIdentifier(QStringLiteral("source"))), Attr_ReadOnly, &index); Q_ASSERT(index == RegExpObject::Index_Source); @@ -311,7 +319,9 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) internalClasses[EngineBase::Class_RegExpExecArray] = internalClasses[EngineBase::Class_RegExpExecArray]->addMember(id_input(), Attr_Data, &index); Q_ASSERT(index == RegExpObject::Index_ArrayInput); - internalClasses[EngineBase::Class_ErrorObject] = internalClasses[Class_Object]->addMember((str = newIdentifier(QStringLiteral("stack"))), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable, &index); + internalClasses[EngineBase::Class_ErrorObject] = + internalClasses[Class_Empty]->changeVTable(QV4::ErrorObject::staticVTable()) + ->addMember((str = newIdentifier(QStringLiteral("stack"))), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorObject::Index_Stack); internalClasses[EngineBase::Class_ErrorObject] = internalClasses[EngineBase::Class_ErrorObject]->addMember((str = newIdentifier(QStringLiteral("fileName"))), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorObject::Index_FileName); @@ -319,7 +329,9 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) Q_ASSERT(index == ErrorObject::Index_LineNumber); internalClasses[EngineBase::Class_ErrorObjectWithMessage] = internalClasses[EngineBase::Class_ErrorObject]->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorObject::Index_Message); - internalClasses[EngineBase::Class_ErrorProto] = internalClasses[Class_Object]->addMember(id_constructor(), Attr_Data|Attr_NotEnumerable, &index); + internalClasses[EngineBase::Class_ErrorProto] = + internalClasses[Class_Empty]->changeVTable(QV4::ErrorPrototype::staticVTable()) + ->addMember(id_constructor(), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorPrototype::Index_Constructor); internalClasses[EngineBase::Class_ErrorProto] = internalClasses[EngineBase::Class_ErrorProto]->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorPrototype::Index_Message); @@ -337,10 +349,10 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) jsObjects[TypeErrorProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_ErrorProto], errorPrototype()); jsObjects[URIErrorProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_ErrorProto], errorPrototype()); - jsObjects[VariantProto] = memoryManager->allocObject(internalClasses[Class_Object], objectPrototype()); + jsObjects[VariantProto] = memoryManager->allocObject(); Q_ASSERT(variantPrototype()->prototype() == objectPrototype()->d()); - jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject(internalClasses[Class_ArrayObject], arrayPrototype())); + jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject()); ExecutionContext *global = rootContext(); jsObjects[Object_Ctor] = memoryManager->allocObject(global); diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h index 783ddc5bd3..e124adb810 100644 --- a/src/qml/jsruntime/qv4enginebase_p.h +++ b/src/qml/jsruntime/qv4enginebase_p.h @@ -87,14 +87,16 @@ struct EngineBase { Class_MemberData, Class_SimpleArrayData, Class_SparseArrayData, + Class_ExecutionContext, Class_Object, Class_ArrayObject, Class_FunctionObject, Class_StringObject, Class_ScriptFunction, Class_ObjectProto, - Class_RegExpExecArray, + Class_RegExp, Class_RegExpObject, + Class_RegExpExecArray, Class_ArgumentsObject, Class_StrictArgumentsObject, Class_ErrorObject, diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp index 63b778b56d..65507e2251 100644 --- a/src/qml/jsruntime/qv4errorobject.cpp +++ b/src/qml/jsruntime/qv4errorobject.cpp @@ -183,8 +183,6 @@ void ErrorObject::markObjects(Heap::Base *that, ExecutionEngine *e) DEFINE_OBJECT_VTABLE(ErrorObject); -DEFINE_OBJECT_VTABLE(SyntaxErrorObject); - void Heap::SyntaxErrorObject::init(const Value &msg) { Heap::ErrorObject::init(msg, SyntaxError); diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h index 896b54da5e..34e4b4a682 100644 --- a/src/qml/jsruntime/qv4errorobject_p.h +++ b/src/qml/jsruntime/qv4errorobject_p.h @@ -203,8 +203,10 @@ struct ReferenceErrorObject: ErrorObject { }; struct SyntaxErrorObject: ErrorObject { - V4_OBJECT2(SyntaxErrorObject, ErrorObject) + typedef Heap::SyntaxErrorObject Data; V4_PROTOTYPE(syntaxErrorPrototype) + const Data *d() const { return static_cast(ErrorObject::d()); } + Data *d() { return static_cast(ErrorObject::d()); } }; struct TypeErrorObject: ErrorObject { diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index e117da1a04..9a4087485f 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -249,8 +249,17 @@ InternalClass *InternalClass::changeVTableImpl(const VTable *vt) // create a new class and add it to the tree InternalClass *newClass; - newClass = engine->newClass(*this); - newClass->vtable = vt; + if (this == engine->internalClasses[EngineBase::Class_Empty]) { + newClass = engine->newClass(*this); + newClass->vtable = vt; + } else { + newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vt); + newClass = newClass->changePrototype(prototype); + for (uint i = 0; i < size; ++i) { + if (!propertyData.at(i).isEmpty()) + newClass = newClass->addMember(nameMap.at(i), propertyData.at(i)); + } + } t.lookup = newClass; Q_ASSERT(t.lookup); diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index c68a6638e7..3c4e0838d9 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -280,7 +280,7 @@ struct InternalClass : public QQmlJS::Managed { void destroy(); private: - InternalClass *changeVTableImpl(const VTable *vt); + Q_QML_EXPORT InternalClass *changeVTableImpl(const VTable *vt); InternalClass *addMemberImpl(Identifier *identifier, PropertyAttributes data, uint *index); friend struct ExecutionEngine; InternalClass(ExecutionEngine *engine); diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h index 00bfad78dd..814755efe9 100644 --- a/src/qml/jsruntime/qv4managed_p.h +++ b/src/qml/jsruntime/qv4managed_p.h @@ -198,6 +198,8 @@ public: }; Q_MANAGED_TYPE(Invalid) + InternalClass *internalClass() const { return d()->internalClass; } + bool isListType() const { return d()->vtable()->type == Type_QmlSequence; } bool isArrayObject() const { return d()->vtable()->type == Type_ArrayObject; } diff --git a/src/qml/jsruntime/qv4numberobject_p.h b/src/qml/jsruntime/qv4numberobject_p.h index 364b866a16..85d306020c 100644 --- a/src/qml/jsruntime/qv4numberobject_p.h +++ b/src/qml/jsruntime/qv4numberobject_p.h @@ -85,6 +85,7 @@ struct NumberCtor: FunctionObject struct NumberPrototype: NumberObject { + V4_PROTOTYPE(objectPrototype) void init(ExecutionEngine *engine, Object *ctor); static void method_isFinite(const BuiltinFunction *, Scope &scope, CallData *callData); diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index f5dafa7914..838ae96c59 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -61,6 +61,7 @@ DEFINE_OBJECT_VTABLE(Object); void Object::setInternalClass(InternalClass *ic) { d()->internalClass = ic; + Q_ASSERT(ic && ic->vtable); uint nInline = d()->vtable()->nInlineProperties; if (ic->size <= nInline) return; diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 78ee263c80..951659a4bc 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -95,7 +95,6 @@ struct Object : Base { return memberData->data + index; } - InternalClass *internalClass; Pointer prototype; Pointer memberData; Pointer arrayData; @@ -204,7 +203,6 @@ struct Q_QML_EXPORT Object: Managed { SetterOffset = 1 }; - InternalClass *internalClass() const { return d()->internalClass; } void setInternalClass(InternalClass *ic); const Value *propertyData(uint index) const { return d()->propertyData(index); } diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index 889f4ea288..ef1a1c11ed 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -331,6 +331,7 @@ Heap::QmlContext *QmlContext::createWorkerContext(ExecutionContext *parent, cons qml->setReadOnly(true); Heap::QmlContext *c = parent->d()->engine->memoryManager->alloc(parent, qml); + Q_ASSERT(c->vtable() == staticVTable()); return c; } @@ -340,6 +341,7 @@ Heap::QmlContext *QmlContext::create(ExecutionContext *parent, QQmlContextData * Scoped qml(scope, scope.engine->memoryManager->allocObject(context, scopeObject)); Heap::QmlContext *c = parent->d()->engine->memoryManager->alloc(parent, qml); + Q_ASSERT(c->vtable() == staticVTable()); return c; } diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 67b1356e65..f484d56040 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -997,7 +997,6 @@ void QObjectWrapper::destroyObject(bool lastCall) } } - h->internalClass = 0; h->~Data(); } diff --git a/src/qml/jsruntime/qv4regexp_p.h b/src/qml/jsruntime/qv4regexp_p.h index d3e63375a5..04cdb468bf 100644 --- a/src/qml/jsruntime/qv4regexp_p.h +++ b/src/qml/jsruntime/qv4regexp_p.h @@ -100,6 +100,7 @@ struct RegExp : public Managed V4_MANAGED(RegExp, Managed) Q_MANAGED_TYPE(RegExp) V4_NEEDS_DESTROY + V4_INTERNALCLASS(RegExp) QString pattern() const { return *d()->pattern; } JSC::Yarr::BytecodePattern *byteCode() { return d()->byteCode; } diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h index 6f96b9f760..0879f149fa 100644 --- a/src/qml/jsruntime/qv4sequenceobject_p.h +++ b/src/qml/jsruntime/qv4sequenceobject_p.h @@ -65,6 +65,7 @@ namespace QV4 { struct SequencePrototype : public QV4::Object { + V4_PROTOTYPE(arrayPrototype) void init(); static void method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData) diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index bf8c48ceee..ad30165ce5 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -72,7 +72,6 @@ struct Q_QML_PRIVATE_EXPORT String : Base { }; #ifndef V4_BOOTSTRAP - V4_INTERNALCLASS(String) void init(MemoryManager *mm, const QString &text); void init(MemoryManager *mm, String *l, String *n); void destroy(); @@ -140,6 +139,7 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed { #ifndef V4_BOOTSTRAP V4_MANAGED(String, Managed) Q_MANAGED_TYPE(String) + V4_INTERNALCLASS(String) V4_NEEDS_DESTROY enum { IsString = true diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index 542460ee81..bb28a7683c 100644 --- a/src/qml/jsruntime/qv4typedarray.cpp +++ b/src/qml/jsruntime/qv4typedarray.cpp @@ -372,7 +372,8 @@ void Heap::TypedArray::init(Type t) Heap::TypedArray *TypedArray::create(ExecutionEngine *e, Heap::TypedArray::Type t) { - return e->memoryManager->allocObject(e->internalClasses[EngineBase::Class_Object], e->typedArrayPrototype + t, t); + QV4::InternalClass *ic = e->internalClasses[EngineBase::Class_Empty]->changeVTable(staticVTable()); + return e->memoryManager->allocObject(ic, e->typedArrayPrototype + t, t); } void TypedArray::markObjects(Heap::Base *that, ExecutionEngine *e) diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h index eefed2db4b..a6a74e4b9b 100644 --- a/src/qml/jsruntime/qv4typedarray_p.h +++ b/src/qml/jsruntime/qv4typedarray_p.h @@ -148,6 +148,7 @@ struct TypedArrayCtor: FunctionObject struct TypedArrayPrototype : Object { V4_OBJECT2(TypedArrayPrototype, Object) + V4_PROTOTYPE(objectPrototype) void init(ExecutionEngine *engine, TypedArrayCtor *ctor); diff --git a/src/qml/jsruntime/qv4variantobject_p.h b/src/qml/jsruntime/qv4variantobject_p.h index ef51b6632d..dba14b13fb 100644 --- a/src/qml/jsruntime/qv4variantobject_p.h +++ b/src/qml/jsruntime/qv4variantobject_p.h @@ -105,6 +105,7 @@ struct VariantObject : Object struct VariantPrototype : VariantObject { public: + V4_PROTOTYPE(objectPrototype) void init(); static void method_preserve(const BuiltinFunction *, Scope &scope, CallData *callData); diff --git a/src/qml/memory/qv4heap_p.h b/src/qml/memory/qv4heap_p.h index cd0a6d9a81..a4e96b4c84 100644 --- a/src/qml/memory/qv4heap_p.h +++ b/src/qml/memory/qv4heap_p.h @@ -53,6 +53,7 @@ #include #include #include +#include #include // To check if Heap::Base::init is called (meaning, all subclasses did their init and called their @@ -69,6 +70,8 @@ QT_BEGIN_NAMESPACE namespace QV4 { +struct InternalClass; + struct VTable { const VTable * const parent; @@ -93,13 +96,12 @@ namespace Heap { struct Q_QML_EXPORT Base { void *operator new(size_t) = delete; - const VTable *vt; + InternalClass *internalClass; inline ReturnedValue asReturnedValue() const; inline void mark(QV4::ExecutionEngine *engine); - void setVtable(const VTable *v) { vt = v; } - const VTable *vtable() const { return vt; } + const VTable *vtable() const { return internalClass->vtable; } inline bool isMarked() const { const HeapItem *h = reinterpret_cast(this); Chunk *c = h->chunk(); @@ -166,7 +168,7 @@ V4_ASSERT_IS_TRIVIAL(Base) // for a size/offset translation when cross-compiling between 32- and // 64-bit. Q_STATIC_ASSERT(std::is_standard_layout::value); -Q_STATIC_ASSERT(offsetof(Base, vt) == 0); +Q_STATIC_ASSERT(offsetof(Base, internalClass) == 0); Q_STATIC_ASSERT(sizeof(Base) == QT_POINTER_SIZE); template diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index ef36e62373..56f1254421 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -787,7 +787,8 @@ Heap::Object *MemoryManager::allocObjectWithMemberData(const QV4::VTable *vtable m = *blockAllocator.allocate(memberSize, true); memset(m, 0, memberSize); o->memberData = static_cast(m); - o->memberData->setVtable(MemberData::staticVTable()); + o->memberData->internalClass = engine->internalClasses[EngineBase::Class_MemberData]; + Q_ASSERT(o->memberData->internalClass); o->memberData->size = static_cast((memberSize - sizeof(Heap::MemberData) + sizeof(Value))/sizeof(Value)); o->memberData->init(); // qDebug() << " got" << o->memberData << o->memberData->size; diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h index 9b4c2cd8df..8f12fa7cbd 100644 --- a/src/qml/memory/qv4mm_p.h +++ b/src/qml/memory/qv4mm_p.h @@ -210,7 +210,8 @@ public: { Heap::CallContext *ctxt = stackAllocator.allocate(); memset(ctxt, 0, sizeof(Heap::CallContext)); - ctxt->setVtable(QV4::CallContext::staticVTable()); + ctxt->internalClass = CallContext::defaultInternalClass(engine); + Q_ASSERT(ctxt->internalClass && ctxt->internalClass->vtable); ctxt->init(v4); return ctxt; @@ -224,7 +225,10 @@ public: V4_ASSERT_IS_TRIVIAL(typename ManagedType::Data) size = align(size); Heap::Base *o = allocData(size); - o->setVtable(ManagedType::staticVTable()); + InternalClass *ic = ManagedType::defaultInternalClass(engine); + ic = ic->changeVTable(ManagedType::staticVTable()); + o->internalClass = ic; + Q_ASSERT(o->internalClass && o->internalClass->vtable); return static_cast(o); } @@ -232,8 +236,9 @@ public: typename ObjectType::Data *allocateObject(InternalClass *ic) { Heap::Object *o = allocObjectWithMemberData(ObjectType::staticVTable(), ic->size); - o->setVtable(ObjectType::staticVTable()); o->internalClass = ic; + Q_ASSERT(o->internalClass && o->internalClass->vtable); + Q_ASSERT(ic->vtable == ObjectType::staticVTable()); return static_cast(o); } @@ -241,10 +246,11 @@ public: typename ObjectType::Data *allocateObject() { InternalClass *ic = ObjectType::defaultInternalClass(engine); + ic = ic->changeVTable(ObjectType::staticVTable()); Heap::Object *o = allocObjectWithMemberData(ObjectType::staticVTable(), ic->size); - o->setVtable(ObjectType::staticVTable()); Object *prototype = ObjectType::defaultPrototype(engine); o->internalClass = ic; + Q_ASSERT(o->internalClass && o->internalClass->vtable); o->prototype = prototype->d(); return static_cast(o); } @@ -253,7 +259,8 @@ public: typename ManagedType::Data *allocWithStringData(std::size_t unmanagedSize, Arg1 arg1) { typename ManagedType::Data *o = reinterpret_cast(allocString(unmanagedSize)); - o->setVtable(ManagedType::staticVTable()); + o->internalClass = ManagedType::defaultInternalClass(engine); + Q_ASSERT(o->internalClass && o->internalClass->vtable); o->init(this, arg1); return o; } -- cgit v1.2.3 From afbb57ae84ecbee5fab9eb6e58356b19d7995ea5 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 15 May 2017 09:56:05 +0200 Subject: Move the prototype into the internal class This saves another pointer on all Objects. Currently introduces a slight performance regression on some of the v8 benchmarks, that needs addressing. Change-Id: I87de8e1d198d2683f4e903c467ce2a60ba542243 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4context_p.h | 1 + src/qml/jsruntime/qv4engine.cpp | 106 ++++++++++++++++++-------------- src/qml/jsruntime/qv4engine_p.h | 2 + src/qml/jsruntime/qv4enginebase_p.h | 2 + src/qml/jsruntime/qv4errorobject.cpp | 3 +- src/qml/jsruntime/qv4errorobject_p.h | 18 +++--- src/qml/jsruntime/qv4functionobject.cpp | 17 +++-- src/qml/jsruntime/qv4functionobject_p.h | 5 +- src/qml/jsruntime/qv4internalclass.cpp | 53 +++++++++++++++- src/qml/jsruntime/qv4internalclass_p.h | 26 +++++--- src/qml/jsruntime/qv4lookup.cpp | 60 +++++++++--------- src/qml/jsruntime/qv4object.cpp | 15 +++-- src/qml/jsruntime/qv4object_p.h | 4 +- src/qml/jsruntime/qv4stringobject_p.h | 1 + src/qml/jsruntime/qv4typedarray.cpp | 1 + src/qml/memory/qv4mm_p.h | 19 +++--- 16 files changed, 215 insertions(+), 118 deletions(-) (limited to 'src') diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index c742df7b97..89ff6dc957 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -272,6 +272,7 @@ struct Q_QML_EXPORT ExecutionContext : public Managed struct Q_QML_EXPORT CallContext : public ExecutionContext { V4_MANAGED(CallContext, ExecutionContext) + V4_INTERNALCLASS(CallContext) // formals are in reverse order Identifier * const *formals() const; diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 6b05d9a05c..daab7ad279 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -213,12 +213,12 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) classPool = new InternalClassPool; internalClasses[Class_Empty] = new (classPool) InternalClass(this); - internalClasses[Class_Object] = internalClasses[Class_Empty]->changeVTable(QV4::Object::staticVTable()); internalClasses[Class_String] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::String::staticVTable()); internalClasses[Class_MemberData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::MemberData::staticVTable()); internalClasses[Class_SimpleArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SimpleArrayData::staticVTable()); internalClasses[Class_SparseArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SparseArrayData::staticVTable()); internalClasses[Class_ExecutionContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::ExecutionContext::staticVTable()); + internalClasses[Class_CallContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::CallContext::staticVTable()); jsStrings[String_Empty] = newIdentifier(QString()); jsStrings[String_undefined] = newIdentifier(QStringLiteral("undefined")); @@ -257,15 +257,20 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) jsStrings[String_buffer] = newIdentifier(QStringLiteral("buffer")); jsStrings[String_lastIndex] = newIdentifier(QStringLiteral("lastIndex")); - jsObjects[ObjectProto] = memoryManager->allocObject(internalClasses[Class_Object]); + InternalClass *ic = internalClasses[Class_Empty]->changeVTable(QV4::Object::staticVTable()); + jsObjects[ObjectProto] = memoryManager->allocObject(ic); + internalClasses[Class_Object] = ic->changePrototype(objectPrototype()->d()); - internalClasses[Class_ArrayObject] = internalClasses[Class_Empty]->changeVTable(QV4::ArrayObject::staticVTable()); - internalClasses[Class_ArrayObject] = internalClasses[Class_ArrayObject]->addMember(id_length(), Attr_NotConfigurable|Attr_NotEnumerable); - jsObjects[ArrayProto] = memoryManager->allocObject(internalClasses[Class_ArrayObject], objectPrototype()); + ic = newInternalClass(ArrayPrototype::staticVTable(), objectPrototype()); + Q_ASSERT(ic->prototype); + ic = ic->addMember(id_length(), Attr_NotConfigurable|Attr_NotEnumerable); + Q_ASSERT(ic->prototype); + jsObjects[ArrayProto] = memoryManager->allocObject(ic, objectPrototype()); + internalClasses[Class_ArrayObject] = ic->changePrototype(arrayPrototype()->d()); jsObjects[PropertyListProto] = memoryManager->allocObject(); - InternalClass *argsClass = internalClasses[Class_Empty]->changeVTable(QV4::ArgumentsObject::staticVTable()) - ->addMember(id_length(), Attr_NotEnumerable); + InternalClass *argsClass = newInternalClass(ArgumentsObject::staticVTable(), objectPrototype()); + argsClass = argsClass->addMember(id_length(), Attr_NotEnumerable); internalClasses[EngineBase::Class_ArgumentsObject] = argsClass->addMember(id_callee(), Attr_Data|Attr_NotEnumerable); argsClass = argsClass->addMember(id_callee(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); internalClasses[EngineBase::Class_StrictArgumentsObject] = argsClass->addMember(id_caller(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); @@ -274,25 +279,31 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) Q_ASSERT(globalObject->d()->vtable()); initRootContext(); - internalClasses[Class_StringObject] = internalClasses[Class_Empty]->changeVTable(QV4::StringObject::staticVTable()); - internalClasses[EngineBase::Class_StringObject] = internalClasses[Class_StringObject]->addMember(id_length(), Attr_ReadOnly); + ic = newInternalClass(QV4::StringObject::staticVTable(), objectPrototype()); + ic = ic->addMember(id_length(), Attr_ReadOnly); + jsObjects[StringProto] = memoryManager->allocObject(ic); + internalClasses[Class_StringObject] = ic->changePrototype(stringPrototype()->d()); Q_ASSERT(internalClasses[EngineBase::Class_StringObject]->find(id_length()) == Heap::StringObject::LengthPropertyIndex); - jsObjects[StringProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_StringObject], objectPrototype()); + jsObjects[NumberProto] = memoryManager->allocObject(); jsObjects[BooleanProto] = memoryManager->allocObject(); jsObjects[DateProto] = memoryManager->allocObject(); uint index; - InternalClass *functionProtoClass = - internalClasses[Class_Empty]->changeVTable(QV4::FunctionPrototype::staticVTable()) - ->addMember(id_prototype(), Attr_NotEnumerable, &index); + ic = newInternalClass(QV4::FunctionPrototype::staticVTable(), objectPrototype()); + ic = ic->addMember(id_prototype(), Attr_NotEnumerable, &index); Q_ASSERT(index == Heap::FunctionObject::Index_Prototype); - jsObjects[FunctionProto] = memoryManager->allocObject(functionProtoClass, objectPrototype()); - internalClasses[EngineBase::Class_FunctionObject] = internalClasses[Class_Object]->addMember(id_prototype(), Attr_NotEnumerable|Attr_NotConfigurable, &index); + jsObjects[FunctionProto] = memoryManager->allocObject(ic, objectPrototype()); + ic = newInternalClass(FunctionObject::staticVTable(), functionPrototype()); + ic = ic->addMember(id_prototype(), Attr_NotEnumerable|Attr_NotConfigurable, &index); Q_ASSERT(index == Heap::FunctionObject::Index_Prototype); - internalClasses[EngineBase::Class_ScriptFunction] = internalClasses[EngineBase::Class_FunctionObject]->addMember(id_name(), Attr_ReadOnly, &index); + internalClasses[EngineBase::Class_FunctionObject] = ic; + ic = ic->addMember(id_name(), Attr_ReadOnly, &index); Q_ASSERT(index == Heap::ScriptFunction::Index_Name); - internalClasses[EngineBase::Class_ScriptFunction] = internalClasses[EngineBase::Class_ScriptFunction]->addMember(id_length(), Attr_ReadOnly, &index); + ic = ic->changeVTable(ScriptFunction::staticVTable()); + internalClasses[EngineBase::Class_ScriptFunction] = ic->addMember(id_length(), Attr_ReadOnly, &index); + Q_ASSERT(index == Heap::ScriptFunction::Index_Length); + internalClasses[EngineBase::Class_BuiltinFunction] = ic->changeVTable(BuiltinFunction::staticVTable()); Q_ASSERT(index == Heap::ScriptFunction::Index_Length); internalClasses[EngineBase::Class_ObjectProto] = internalClasses[Class_Object]->addMember(id_constructor(), Attr_NotEnumerable, &index); Q_ASSERT(index == Heap::FunctionObject::Index_ProtoConstructor); @@ -300,59 +311,59 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) Scope scope(this); ScopedString str(scope); internalClasses[Class_RegExp] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::RegExp::staticVTable()); - internalClasses[EngineBase::Class_RegExpObject] = - internalClasses[Class_Empty]->changeVTable(QV4::RegExpObject::staticVTable()) - ->addMember(id_lastIndex(), Attr_NotEnumerable|Attr_NotConfigurable, &index); + ic = newInternalClass(QV4::RegExpObject::staticVTable(), objectPrototype()); + ic = ic->addMember(id_lastIndex(), Attr_NotEnumerable|Attr_NotConfigurable, &index); Q_ASSERT(index == RegExpObject::Index_LastIndex); - internalClasses[EngineBase::Class_RegExpObject] = internalClasses[EngineBase::Class_RegExpObject]->addMember((str = newIdentifier(QStringLiteral("source"))), Attr_ReadOnly, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("source"))), Attr_ReadOnly, &index); Q_ASSERT(index == RegExpObject::Index_Source); - internalClasses[EngineBase::Class_RegExpObject] = internalClasses[EngineBase::Class_RegExpObject]->addMember((str = newIdentifier(QStringLiteral("global"))), Attr_ReadOnly, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("global"))), Attr_ReadOnly, &index); Q_ASSERT(index == RegExpObject::Index_Global); - internalClasses[EngineBase::Class_RegExpObject] = internalClasses[EngineBase::Class_RegExpObject]->addMember((str = newIdentifier(QStringLiteral("ignoreCase"))), Attr_ReadOnly, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("ignoreCase"))), Attr_ReadOnly, &index); Q_ASSERT(index == RegExpObject::Index_IgnoreCase); - internalClasses[EngineBase::Class_RegExpObject] = internalClasses[EngineBase::Class_RegExpObject]->addMember((str = newIdentifier(QStringLiteral("multiline"))), Attr_ReadOnly, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("multiline"))), Attr_ReadOnly, &index); Q_ASSERT(index == RegExpObject::Index_Multiline); + jsObjects[RegExpProto] = memoryManager->allocObject(ic, objectPrototype()); + internalClasses[Class_RegExpObject] = ic->changePrototype(regExpPrototype()->d()); - jsObjects[RegExpProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_RegExpObject], objectPrototype()); - internalClasses[EngineBase::Class_RegExpExecArray] = internalClasses[Class_ArrayObject]->addMember(id_index(), Attr_Data, &index); + ic = internalClasses[Class_ArrayObject]->addMember(id_index(), Attr_Data, &index); Q_ASSERT(index == RegExpObject::Index_ArrayIndex); - internalClasses[EngineBase::Class_RegExpExecArray] = internalClasses[EngineBase::Class_RegExpExecArray]->addMember(id_input(), Attr_Data, &index); + internalClasses[EngineBase::Class_RegExpExecArray] = ic->addMember(id_input(), Attr_Data, &index); Q_ASSERT(index == RegExpObject::Index_ArrayInput); - internalClasses[EngineBase::Class_ErrorObject] = - internalClasses[Class_Empty]->changeVTable(QV4::ErrorObject::staticVTable()) - ->addMember((str = newIdentifier(QStringLiteral("stack"))), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable, &index); + ic = newInternalClass(ErrorObject::staticVTable(), 0); + ic = ic->addMember((str = newIdentifier(QStringLiteral("stack"))), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorObject::Index_Stack); - internalClasses[EngineBase::Class_ErrorObject] = internalClasses[EngineBase::Class_ErrorObject]->addMember((str = newIdentifier(QStringLiteral("fileName"))), Attr_Data|Attr_NotEnumerable, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("fileName"))), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorObject::Index_FileName); - internalClasses[EngineBase::Class_ErrorObject] = internalClasses[EngineBase::Class_ErrorObject]->addMember((str = newIdentifier(QStringLiteral("lineNumber"))), Attr_Data|Attr_NotEnumerable, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("lineNumber"))), Attr_Data|Attr_NotEnumerable, &index); + internalClasses[EngineBase::Class_ErrorObject] = ic; Q_ASSERT(index == ErrorObject::Index_LineNumber); - internalClasses[EngineBase::Class_ErrorObjectWithMessage] = internalClasses[EngineBase::Class_ErrorObject]->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index); + internalClasses[EngineBase::Class_ErrorObjectWithMessage] = ic->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorObject::Index_Message); - internalClasses[EngineBase::Class_ErrorProto] = - internalClasses[Class_Empty]->changeVTable(QV4::ErrorPrototype::staticVTable()) - ->addMember(id_constructor(), Attr_Data|Attr_NotEnumerable, &index); + ic = newInternalClass(ErrorObject::staticVTable(), objectPrototype()); + ic = ic->addMember(id_constructor(), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorPrototype::Index_Constructor); - internalClasses[EngineBase::Class_ErrorProto] = internalClasses[EngineBase::Class_ErrorProto]->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorPrototype::Index_Message); - internalClasses[EngineBase::Class_ErrorProto] = internalClasses[EngineBase::Class_ErrorProto]->addMember(id_name(), Attr_Data|Attr_NotEnumerable, &index); + internalClasses[EngineBase::Class_ErrorProto] = ic->addMember(id_name(), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorPrototype::Index_Name); jsObjects[GetStack_Function] = BuiltinFunction::create(rootContext(), str = newIdentifier(QStringLiteral("stack")), ErrorObject::method_get_stack); getStackFunction()->defineReadonlyProperty(id_length(), Primitive::fromInt32(0)); jsObjects[ErrorProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_ErrorProto], objectPrototype()); - jsObjects[EvalErrorProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_ErrorProto], errorPrototype()); - jsObjects[RangeErrorProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_ErrorProto], errorPrototype()); - jsObjects[ReferenceErrorProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_ErrorProto], errorPrototype()); - jsObjects[SyntaxErrorProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_ErrorProto], errorPrototype()); - jsObjects[TypeErrorProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_ErrorProto], errorPrototype()); - jsObjects[URIErrorProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_ErrorProto], errorPrototype()); + jsObjects[EvalErrorProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype()); + jsObjects[RangeErrorProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype()); + jsObjects[ReferenceErrorProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype()); + jsObjects[SyntaxErrorProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype()); + jsObjects[TypeErrorProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype()); + jsObjects[URIErrorProto] = memoryManager->allocObject(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype()); jsObjects[VariantProto] = memoryManager->allocObject(); Q_ASSERT(variantPrototype()->prototype() == objectPrototype()->d()); - jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject()); + ic = newInternalClass(SequencePrototype::staticVTable(), SequencePrototype::defaultPrototype(this)); + jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject(ic, SequencePrototype::defaultPrototype(this))); ExecutionContext *global = rootContext(); jsObjects[Object_Ctor] = memoryManager->allocObject(global); @@ -538,6 +549,11 @@ ExecutionContext *ExecutionEngine::pushGlobalContext() return currentContext; } +InternalClass *ExecutionEngine::newInternalClass(const VTable *vtable, Object *prototype) +{ + return internalClasses[EngineBase::Class_Empty]->changeVTable(vtable)->changePrototype(prototype ? prototype->d() : 0); +} + Heap::Object *ExecutionEngine::newObject() { return memoryManager->allocObject(); diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 1d07196c28..a569b8ddf9 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -366,6 +366,8 @@ public: void popContext(); ExecutionContext *parentContext(ExecutionContext *context) const; + InternalClass *newInternalClass(const VTable *vtable, Object *prototype); + Heap::Object *newObject(); Heap::Object *newObject(InternalClass *internalClass, Object *prototype); diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h index e124adb810..06c346b3c0 100644 --- a/src/qml/jsruntime/qv4enginebase_p.h +++ b/src/qml/jsruntime/qv4enginebase_p.h @@ -88,11 +88,13 @@ struct EngineBase { Class_SimpleArrayData, Class_SparseArrayData, Class_ExecutionContext, + Class_CallContext, Class_Object, Class_ArrayObject, Class_FunctionObject, Class_StringObject, Class_ScriptFunction, + Class_BuiltinFunction, Class_ObjectProto, Class_RegExp, Class_RegExpObject, diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp index 65507e2251..6811438915 100644 --- a/src/qml/jsruntime/qv4errorobject.cpp +++ b/src/qml/jsruntime/qv4errorobject.cpp @@ -328,8 +328,7 @@ void ErrorPrototype::init(ExecutionEngine *engine, Object *ctor, Object *obj, He *obj->propertyData(Index_Constructor) = ctor; *obj->propertyData(Index_Message) = engine->id_empty(); *obj->propertyData(Index_Name) = engine->newString(QString::fromLatin1(ErrorObject::className(t))); - if (t == Heap::ErrorObject::Error) - obj->defineDefaultProperty(engine->id_toString(), method_toString, 0); + obj->defineDefaultProperty(engine->id_toString(), method_toString, 0); } void ErrorPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h index 34e4b4a682..12531c9309 100644 --- a/src/qml/jsruntime/qv4errorobject_p.h +++ b/src/qml/jsruntime/qv4errorobject_p.h @@ -326,25 +326,25 @@ inline SyntaxErrorObject *ErrorObject::asSyntaxError() template Heap::Object *ErrorObject::create(ExecutionEngine *e, const Value &message) { - return e->memoryManager->allocObject( - e->internalClasses[message.isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage], - T::defaultPrototype(e), message); + InternalClass *ic = e->internalClasses[message.isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage]; + ic = ic->changePrototype(T::defaultPrototype(e)->d()); + return e->memoryManager->allocObject(ic, T::defaultPrototype(e), message); } template Heap::Object *ErrorObject::create(ExecutionEngine *e, const QString &message) { Scope scope(e); ScopedValue v(scope, message.isEmpty() ? Encode::undefined() : e->newString(message)->asReturnedValue()); - return e->memoryManager->allocObject( - e->internalClasses[v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage], - T::defaultPrototype(e), v); + InternalClass *ic = e->internalClasses[v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage]; + ic = ic->changePrototype(T::defaultPrototype(e)->d()); + return e->memoryManager->allocObject(ic, T::defaultPrototype(e), v); } template Heap::Object *ErrorObject::create(ExecutionEngine *e, const QString &message, const QString &filename, int line, int column) { Scope scope(e); ScopedValue v(scope, message.isEmpty() ? Encode::undefined() : e->newString(message)->asReturnedValue()); - return e->memoryManager->allocObject( - e->internalClasses[v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage], - T::defaultPrototype(e), v, filename, line, column); + InternalClass *ic = e->internalClasses[v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage]; + ic = ic->changePrototype(T::defaultPrototype(e)->d()); + return e->memoryManager->allocObject(ic, T::defaultPrototype(e), v, filename, line, column); } diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 86362fa0cb..4be14c09ba 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -375,8 +375,8 @@ void ScriptFunction::construct(const Managed *that, Scope &scope, CallData *call Scoped f(scope, static_cast(that)); - InternalClass *ic = v4->internalClasses[EngineBase::Class_Object]; - ScopedObject proto(scope, f->protoForConstructor()); + InternalClass *ic = f->classForConstructor(); + ScopedObject proto(scope, ic->prototype); ScopedObject obj(scope, v4->newObject(ic, proto)); callData->thisObject = obj.asReturnedValue(); @@ -444,12 +444,19 @@ void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function } } -Heap::Object *ScriptFunction::protoForConstructor() const +InternalClass *ScriptFunction::classForConstructor() const { const Object *o = d()->protoProperty(); + InternalClass *ic = d()->cachedClassForConstructor; + if (ic && ic->prototype == o->d()) + return ic; + + ic = engine()->internalClasses[EngineBase::Class_Object]; if (o) - return o->d(); - return engine()->objectPrototype()->d(); + ic = ic->changePrototype(o->d()); + d()->cachedClassForConstructor = ic; + + return ic; } diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index bfaa1ae056..354f6b2e3f 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -117,6 +117,8 @@ struct ScriptFunction : FunctionObject { Index_Length }; void init(QV4::ExecutionContext *scope, Function *function); + + QV4::InternalClass *cachedClassForConstructor; }; struct BoundFunction : FunctionObject { @@ -199,6 +201,7 @@ struct Q_QML_EXPORT OldBuiltinFunction : FunctionObject { struct Q_QML_EXPORT BuiltinFunction : FunctionObject { V4_OBJECT2(BuiltinFunction, FunctionObject) + V4_INTERNALCLASS(BuiltinFunction) static Heap::OldBuiltinFunction *create(ExecutionContext *scope, String *name, ReturnedValue (*code)(CallContext *)) { @@ -241,7 +244,7 @@ struct ScriptFunction : FunctionObject { static void construct(const Managed *, Scope &scope, CallData *callData); static void call(const Managed *that, Scope &scope, CallData *callData); - Heap::Object *protoForConstructor() const; + InternalClass *classForConstructor() const; }; diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index 9a4087485f..600198567a 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -106,6 +106,7 @@ void PropertyHash::addEntry(const PropertyHash::Entry &entry, int classSize) InternalClass::InternalClass(ExecutionEngine *engine) : engine(engine) , vtable(0) + , prototype(0) , m_sealed(0) , m_frozen(0) , size(0) @@ -118,6 +119,7 @@ InternalClass::InternalClass(const QV4::InternalClass &other) : QQmlJS::Managed() , engine(other.engine) , vtable(other.vtable) + , prototype(other.prototype) , propertyTable(other.propertyTable) , nameMap(other.nameMap) , propertyData(other.propertyData) @@ -223,6 +225,7 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri // create a new class and add it to the tree InternalClass *newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable); + newClass = newClass->changePrototype(prototype); for (uint i = 0; i < size; ++i) { if (i == idx) { newClass = newClass->addMember(nameMap.at(i), data); @@ -236,6 +239,35 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri return newClass; } +InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto) +{ + Q_ASSERT(prototype != proto); + + Transition temp = { { nullptr }, 0, Transition::PrototypeChange }; + temp.prototype = proto; + + Transition &t = lookupOrInsertTransition(temp); + if (t.lookup) + return t.lookup; + + // create a new class and add it to the tree + InternalClass *newClass; + if (!size && !prototype) { + newClass = engine->newClass(*this); + newClass->prototype = proto; + } else { + newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable); + newClass = newClass->changePrototype(proto); + for (uint i = 0; i < size; ++i) { + if (!propertyData.at(i).isEmpty()) + newClass = newClass->addMember(nameMap.at(i), propertyData.at(i)); + } + } + + t.lookup = newClass; + return newClass; +} + InternalClass *InternalClass::changeVTableImpl(const VTable *vt) { Q_ASSERT(vtable != vt); @@ -366,6 +398,7 @@ void InternalClass::removeMember(Object *object, Identifier *id) } else { // create a new class and add it to the tree InternalClass *newClass = oldClass->engine->internalClasses[EngineBase::Class_Empty]->changeVTable(oldClass->vtable); + newClass = newClass->changePrototype(oldClass->prototype); for (uint i = 0; i < oldClass->size; ++i) { if (i == propIdx) continue; @@ -411,6 +444,7 @@ InternalClass *InternalClass::sealed() return m_sealed; m_sealed = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable); + m_sealed = m_sealed->changePrototype(prototype); for (uint i = 0; i < size; ++i) { PropertyAttributes attrs = propertyData.at(i); if (attrs.isEmpty()) @@ -440,6 +474,7 @@ InternalClass *InternalClass::frozen() InternalClass *InternalClass::propertiesFrozen() const { InternalClass *frozen = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable); + frozen = frozen->changePrototype(prototype); for (uint i = 0; i < size; ++i) { PropertyAttributes attrs = propertyData.at(i); if (attrs.isEmpty()) @@ -480,9 +515,25 @@ void InternalClass::destroy() } } +void InternalClass::mark(ExecutionEngine *e) +{ + if (m_sealed) + m_sealed->mark(e); + if (m_frozen) + m_frozen->mark(e); + + for (size_t i = 0; i < transitions.size(); ++i) { + Q_ASSERT(transitions.at(i).lookup); + transitions.at(i).lookup->mark(e); + } + if (prototype) + prototype->mark(engine); +} + void InternalClassPool::markObjects(ExecutionEngine *engine) { - Q_UNUSED(engine); + InternalClass *ic = engine->internalClasses[EngineBase::Class_Empty]; + ic->mark(engine); } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index 3c4e0838d9..031f66793b 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -223,6 +223,7 @@ struct InternalClassTransition union { Identifier *id; const VTable *vtable; + Heap::Object *prototype; }; InternalClass *lookup; int flags; @@ -230,6 +231,7 @@ struct InternalClassTransition // range 0-0xff is reserved for attribute changes NotExtensible = 0x100, VTableChange = 0x200, + PrototypeChange = 0x201 }; bool operator==(const InternalClassTransition &other) const @@ -242,6 +244,7 @@ struct InternalClassTransition struct InternalClass : public QQmlJS::Managed { ExecutionEngine *engine; const VTable *vtable; + Heap::Object *prototype; PropertyHash propertyTable; // id to valueIndex SharedInternalClassData nameMap; @@ -257,30 +260,37 @@ struct InternalClass : public QQmlJS::Managed { uint size; bool extensible; - InternalClass *nonExtensible(); - InternalClass *changeVTable(const VTable *vt) { + Q_REQUIRED_RESULT InternalClass *nonExtensible(); + Q_REQUIRED_RESULT InternalClass *changeVTable(const VTable *vt) { if (vtable == vt) return this; return changeVTableImpl(vt); } + Q_REQUIRED_RESULT InternalClass *changePrototype(Heap::Object *proto) { + if (prototype == proto) + return this; + return changePrototypeImpl(proto); + } static void addMember(Object *object, String *string, PropertyAttributes data, uint *index); - InternalClass *addMember(String *string, PropertyAttributes data, uint *index = 0); - InternalClass *addMember(Identifier *identifier, PropertyAttributes data, uint *index = 0); - InternalClass *changeMember(Identifier *identifier, PropertyAttributes data, uint *index = 0); + Q_REQUIRED_RESULT InternalClass *addMember(String *string, PropertyAttributes data, uint *index = 0); + Q_REQUIRED_RESULT InternalClass *addMember(Identifier *identifier, PropertyAttributes data, uint *index = 0); + Q_REQUIRED_RESULT InternalClass *changeMember(Identifier *identifier, PropertyAttributes data, uint *index = 0); static void changeMember(Object *object, String *string, PropertyAttributes data, uint *index = 0); static void removeMember(Object *object, Identifier *id); uint find(const String *string); uint find(const Identifier *id); - InternalClass *sealed(); - InternalClass *frozen(); - InternalClass *propertiesFrozen() const; + Q_REQUIRED_RESULT InternalClass *sealed(); + Q_REQUIRED_RESULT InternalClass *frozen(); + Q_REQUIRED_RESULT InternalClass *propertiesFrozen() const; void destroy(); + void mark(ExecutionEngine *e); private: Q_QML_EXPORT InternalClass *changeVTableImpl(const VTable *vt); + Q_QML_EXPORT InternalClass *changePrototypeImpl(Heap::Object *proto); InternalClass *addMemberImpl(Identifier *identifier, PropertyAttributes data, uint *index); friend struct ExecutionEngine; InternalClass(ExecutionEngine *engine); diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index f8ac0cb650..25fa670115 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -63,7 +63,7 @@ ReturnedValue Lookup::lookup(const Value &thisObject, Object *o, PropertyAttribu return !attrs->isAccessor() ? v->asReturnedValue() : Object::getValue(thisObject, *v, *attrs); } - obj = obj->prototype; + obj = obj->prototype(); ++i; } level = Size; @@ -76,7 +76,7 @@ ReturnedValue Lookup::lookup(const Value &thisObject, Object *o, PropertyAttribu return !attrs->isAccessor() ? v->asReturnedValue() : Object::getValue(thisObject, *v, *attrs); } - obj = obj->prototype; + obj = obj->prototype(); } return Primitive::emptyValue().asReturnedValue(); } @@ -98,7 +98,7 @@ ReturnedValue Lookup::lookup(const Object *thisObject, PropertyAttributes *attrs return !attrs->isAccessor() ? v->asReturnedValue() : thisObject->getValue(*v, *attrs); } - obj = obj->prototype; + obj = obj->prototype(); ++i; } level = Size; @@ -111,7 +111,7 @@ ReturnedValue Lookup::lookup(const Object *thisObject, PropertyAttributes *attrs return !attrs->isAccessor() ? v->asReturnedValue() : thisObject->getValue(*v, *attrs); } - obj = obj->prototype; + obj = obj->prototype(); } return Primitive::emptyValue().asReturnedValue(); } @@ -402,8 +402,8 @@ ReturnedValue Lookup::getter1(Lookup *l, ExecutionEngine *engine, const Value &o // the internal class won't match Heap::Object *o = static_cast(object.heapObject()); if (o) { - if (l->classList[0] == o->internalClass && l->classList[1] == o->prototype->internalClass) - return o->prototype->propertyData(l->index)->asReturnedValue(); + if (l->classList[0] == o->internalClass && l->classList[1] == o->prototype()->internalClass) + return o->prototype()->propertyData(l->index)->asReturnedValue(); } return getterTwoClasses(l, engine, object); } @@ -415,9 +415,9 @@ ReturnedValue Lookup::getter2(Lookup *l, ExecutionEngine *engine, const Value &o Heap::Object *o = static_cast(object.heapObject()); if (o) { if (l->classList[0] == o->internalClass) { - Heap::Object *p = o->prototype; + Heap::Object *p = o->prototype(); if (l->classList[1] == p->internalClass) { - p = p->prototype; + p = p->prototype(); if (l->classList[2] == p->internalClass) return p->propertyData(l->index)->asReturnedValue(); } @@ -480,8 +480,8 @@ ReturnedValue Lookup::getter0Inlinegetter1(Lookup *l, ExecutionEngine *engine, c if (o) { if (l->classList[0] == o->internalClass) return o->inlinePropertyData(l->index)->asReturnedValue(); - if (l->classList[2] == o->internalClass && l->classList[3] == o->prototype->internalClass) - return o->prototype->propertyData(l->index2)->asReturnedValue(); + if (l->classList[2] == o->internalClass && l->classList[3] == o->prototype()->internalClass) + return o->prototype()->propertyData(l->index2)->asReturnedValue(); } l->getter = getterFallback; return getterFallback(l, engine, object); @@ -495,8 +495,8 @@ ReturnedValue Lookup::getter0MemberDatagetter1(Lookup *l, ExecutionEngine *engin if (o) { if (l->classList[0] == o->internalClass) return o->memberData->data[l->index].asReturnedValue(); - if (l->classList[2] == o->internalClass && l->classList[3] == o->prototype->internalClass) - return o->prototype->propertyData(l->index2)->asReturnedValue(); + if (l->classList[2] == o->internalClass && l->classList[3] == o->prototype()->internalClass) + return o->prototype()->propertyData(l->index2)->asReturnedValue(); } l->getter = getterFallback; return getterFallback(l, engine, object); @@ -509,11 +509,11 @@ ReturnedValue Lookup::getter1getter1(Lookup *l, ExecutionEngine *engine, const V Heap::Object *o = static_cast(object.heapObject()); if (o) { if (l->classList[0] == o->internalClass && - l->classList[1] == o->prototype->internalClass) - return o->prototype->propertyData(l->index)->asReturnedValue(); + l->classList[1] == o->prototype()->internalClass) + return o->prototype()->propertyData(l->index)->asReturnedValue(); if (l->classList[2] == o->internalClass && - l->classList[3] == o->prototype->internalClass) - return o->prototype->propertyData(l->index2)->asReturnedValue(); + l->classList[3] == o->prototype()->internalClass) + return o->prototype()->propertyData(l->index2)->asReturnedValue(); return getterFallback(l, engine, object); } l->getter = getterFallback; @@ -550,9 +550,9 @@ ReturnedValue Lookup::getterAccessor1(Lookup *l, ExecutionEngine *engine, const Heap::Object *o = static_cast(object.heapObject()); if (o) { if (l->classList[0] == o->internalClass && - l->classList[1] == o->prototype->internalClass) { + l->classList[1] == o->prototype()->internalClass) { Scope scope(o->internalClass->engine); - ScopedFunctionObject getter(scope, o->prototype->propertyData(l->index + Object::GetterOffset)); + ScopedFunctionObject getter(scope, o->prototype()->propertyData(l->index + Object::GetterOffset)); if (!getter) return Encode::undefined(); @@ -573,9 +573,9 @@ ReturnedValue Lookup::getterAccessor2(Lookup *l, ExecutionEngine *engine, const Heap::Object *o = static_cast(object.heapObject()); if (o) { if (l->classList[0] == o->internalClass) { - o = o->prototype; + o = o->prototype(); if (l->classList[1] == o->internalClass) { - o = o->prototype; + o = o->prototype(); if (l->classList[2] == o->internalClass) { Scope scope(o->internalClass->engine); ScopedFunctionObject getter(scope, o->propertyData(l->index + Object::GetterOffset)); @@ -621,8 +621,8 @@ ReturnedValue Lookup::primitiveGetter1(Lookup *l, ExecutionEngine *engine, const if (object.type() == l->type) { Heap::Object *o = l->proto; if (l->classList[0] == o->internalClass && - l->classList[1] == o->prototype->internalClass) - return o->prototype->propertyData(l->index)->asReturnedValue(); + l->classList[1] == o->prototype()->internalClass) + return o->prototype()->propertyData(l->index)->asReturnedValue(); } l->getter = getterGeneric; return getterGeneric(l, engine, object); @@ -653,9 +653,9 @@ ReturnedValue Lookup::primitiveGetterAccessor1(Lookup *l, ExecutionEngine *engin if (object.type() == l->type) { Heap::Object *o = l->proto; if (l->classList[0] == o->internalClass && - l->classList[1] == o->prototype->internalClass) { + l->classList[1] == o->prototype()->internalClass) { Scope scope(o->internalClass->engine); - ScopedFunctionObject getter(scope, o->prototype->propertyData(l->index + Object::GetterOffset)); + ScopedFunctionObject getter(scope, o->prototype()->propertyData(l->index + Object::GetterOffset)); if (!getter) return Encode::undefined(); @@ -758,11 +758,11 @@ ReturnedValue Lookup::globalGetter2(Lookup *l, ExecutionEngine *engine) { Heap::Object *o = engine->globalObject->d(); if (l->classList[0] == o->internalClass) { - o = o->prototype; + o = o->prototype(); if (l->classList[1] == o->internalClass) { - o = o->prototype; + o = o->prototype(); if (l->classList[2] == o->internalClass) { - return o->prototype->propertyData(l->index)->asReturnedValue(); + return o->prototype()->propertyData(l->index)->asReturnedValue(); } } } @@ -811,9 +811,9 @@ ReturnedValue Lookup::globalGetterAccessor2(Lookup *l, ExecutionEngine *engine) { Heap::Object *o = engine->globalObject->d(); if (l->classList[0] == o->internalClass) { - o = o->prototype; + o = o->prototype(); if (l->classList[1] == o->internalClass) { - o = o->prototype; + o = o->prototype(); if (l->classList[2] == o->internalClass) { Scope scope(o->internalClass->engine); ScopedFunctionObject getter(scope, o->propertyData(l->index + Object::GetterOffset)); @@ -934,7 +934,7 @@ void Lookup::setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, co if (o && o->internalClass() == l->classList[0]) { Heap::Object *p = o->prototype(); if (p && p->internalClass == l->classList[1]) { - p = p->prototype; + p = p->prototype(); if (p && p->internalClass == l->classList[2]) { o->setInternalClass(l->classList[3]); *o->propertyData(l->index) = value; diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 838ae96c59..4abe508e10 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -88,13 +88,14 @@ void Object::setProperty(uint index, const Property *p) bool Object::setPrototype(Object *proto) { - Heap::Object *pp = proto ? proto->d() : 0; + Heap::Object *p = proto ? proto->d() : 0; + Heap::Object *pp = p; while (pp) { if (pp == d()) return false; - pp = pp->prototype; + pp = pp->prototype(); } - d()->prototype = proto ? proto->d() : 0; + setInternalClass(internalClass()->changePrototype(p)); return true; } @@ -264,8 +265,6 @@ void Object::markObjects(Heap::Base *that, ExecutionEngine *e) o->memberData->mark(e); if (o->arrayData) o->arrayData->mark(e); - if (o->prototype) - o->prototype->mark(e); uint nInline = o->vtable()->nInlineProperties; Value *v = reinterpret_cast(o) + o->vtable()->inlinePropertyOffset; const Value *end = v + nInline; @@ -345,7 +344,7 @@ Value *Object::getValueOrSetter(String *name, PropertyAttributes *attrs) return o->propertyData(attrs->isAccessor() ? idx + SetterOffset : idx); } - o = o->prototype; + o = o->prototype(); } *attrs = Attr_Invalid; return 0; @@ -368,7 +367,7 @@ Value *Object::getValueOrSetter(uint index, PropertyAttributes *attrs) return reinterpret_cast(0x1); } } - o = o->prototype; + o = o->prototype(); } *attrs = Attr_Invalid; return 0; @@ -1204,7 +1203,7 @@ ReturnedValue Object::instanceOf(const Object *typeObject, const Value &var) // 15.3.5.3, 4 while (v) { // 15.3.5.3, 4, a - v = v->prototype; + v = v->prototype(); // 15.3.5.3, 4, b if (!v) diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 951659a4bc..cf04a84175 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -95,7 +95,7 @@ struct Object : Base { return memberData->data + index; } - Pointer prototype; + Heap::Object *prototype() const { return internalClass->prototype; } Pointer memberData; Pointer arrayData; }; @@ -215,7 +215,7 @@ struct Q_QML_EXPORT Object: Managed { void setProperty(uint index, const Property *p); const ObjectVTable *vtable() const { return reinterpret_cast(d()->vtable()); } - Heap::Object *prototype() const { return d()->prototype; } + Heap::Object *prototype() const { return d()->prototype(); } bool setPrototype(Object *proto); void getOwnProperty(String *name, PropertyAttributes *attrs, Property *p = 0); diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h index b8fb80546f..2e64364f9f 100644 --- a/src/qml/jsruntime/qv4stringobject_p.h +++ b/src/qml/jsruntime/qv4stringobject_p.h @@ -109,6 +109,7 @@ struct StringCtor: FunctionObject struct StringPrototype: StringObject { + V4_PROTOTYPE(objectPrototype) void init(ExecutionEngine *engine, Object *ctor); static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index bb28a7683c..80655aded6 100644 --- a/src/qml/jsruntime/qv4typedarray.cpp +++ b/src/qml/jsruntime/qv4typedarray.cpp @@ -373,6 +373,7 @@ void Heap::TypedArray::init(Type t) Heap::TypedArray *TypedArray::create(ExecutionEngine *e, Heap::TypedArray::Type t) { QV4::InternalClass *ic = e->internalClasses[EngineBase::Class_Empty]->changeVTable(staticVTable()); + ic = ic->changePrototype(e->typedArrayPrototype[t].d()); return e->memoryManager->allocObject(ic, e->typedArrayPrototype + t, t); } diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h index 8f12fa7cbd..69d3eeb93c 100644 --- a/src/qml/memory/qv4mm_p.h +++ b/src/qml/memory/qv4mm_p.h @@ -247,11 +247,11 @@ public: { InternalClass *ic = ObjectType::defaultInternalClass(engine); ic = ic->changeVTable(ObjectType::staticVTable()); + ic = ic->changePrototype(ObjectType::defaultPrototype(engine)->d()); Heap::Object *o = allocObjectWithMemberData(ObjectType::staticVTable(), ic->size); - Object *prototype = ObjectType::defaultPrototype(engine); o->internalClass = ic; Q_ASSERT(o->internalClass && o->internalClass->vtable); - o->prototype = prototype->d(); + Q_ASSERT(o->internalClass->prototype == ObjectType::defaultPrototype(engine)->d()); return static_cast(o); } @@ -279,7 +279,8 @@ public: { Scope scope(engine); Scoped t(scope, allocateObject(ic)); - t->d_unchecked()->prototype = prototype->d(); + Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : 0)); + Q_UNUSED(prototype); t->d_unchecked()->init(); return t->d(); } @@ -289,7 +290,8 @@ public: { Scope scope(engine); Scoped t(scope, allocateObject(ic)); - t->d_unchecked()->prototype = prototype->d(); + Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : 0)); + Q_UNUSED(prototype); t->d_unchecked()->init(arg1); return t->d(); } @@ -299,7 +301,8 @@ public: { Scope scope(engine); Scoped t(scope, allocateObject(ic)); - t->d_unchecked()->prototype = prototype->d(); + Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : 0)); + Q_UNUSED(prototype); t->d_unchecked()->init(arg1, arg2); return t->d(); } @@ -309,7 +312,8 @@ public: { Scope scope(engine); Scoped t(scope, allocateObject(ic)); - t->d_unchecked()->prototype = prototype->d(); + Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : 0)); + Q_UNUSED(prototype); t->d_unchecked()->init(arg1, arg2, arg3); return t->d(); } @@ -319,7 +323,8 @@ public: { Scope scope(engine); Scoped t(scope, allocateObject(ic)); - t->d_unchecked()->prototype = prototype->d(); + Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : 0)); + Q_UNUSED(prototype); t->d_unchecked()->init(arg1, arg2, arg3, arg4); return t->d(); } -- cgit v1.2.3 From 10b237882cfe76521b4dc65300a2a0473faca174 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 15 May 2017 12:42:56 +0200 Subject: Optimize lookups based on IC changes Change-Id: I1f4f4aaad0c8194bce2ebde4503df38cab0990a2 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4lookup.cpp | 50 ++++++++++++++++++++++------------------- src/qml/jsruntime/qv4lookup_p.h | 3 ++- src/qml/jsruntime/qv4object.cpp | 2 ++ 3 files changed, 31 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index 25fa670115..faaa5539ab 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -402,8 +402,8 @@ ReturnedValue Lookup::getter1(Lookup *l, ExecutionEngine *engine, const Value &o // the internal class won't match Heap::Object *o = static_cast(object.heapObject()); if (o) { - if (l->classList[0] == o->internalClass && l->classList[1] == o->prototype()->internalClass) - return o->prototype()->propertyData(l->index)->asReturnedValue(); + if (l->classList[0] == o->internalClass && l->classList[1] == l->proto->internalClass) + return l->proto->propertyData(l->index)->asReturnedValue(); } return getterTwoClasses(l, engine, object); } @@ -415,9 +415,9 @@ ReturnedValue Lookup::getter2(Lookup *l, ExecutionEngine *engine, const Value &o Heap::Object *o = static_cast(object.heapObject()); if (o) { if (l->classList[0] == o->internalClass) { - Heap::Object *p = o->prototype(); - if (l->classList[1] == p->internalClass) { - p = p->prototype(); + Q_ASSERT(l->proto == o->prototype()); + if (l->classList[1] == l->proto->internalClass) { + Heap::Object *p = l->proto->prototype(); if (l->classList[2] == p->internalClass) return p->propertyData(l->index)->asReturnedValue(); } @@ -550,7 +550,7 @@ ReturnedValue Lookup::getterAccessor1(Lookup *l, ExecutionEngine *engine, const Heap::Object *o = static_cast(object.heapObject()); if (o) { if (l->classList[0] == o->internalClass && - l->classList[1] == o->prototype()->internalClass) { + l->classList[1] == l->proto->internalClass) { Scope scope(o->internalClass->engine); ScopedFunctionObject getter(scope, o->prototype()->propertyData(l->index + Object::GetterOffset)); if (!getter) @@ -573,9 +573,9 @@ ReturnedValue Lookup::getterAccessor2(Lookup *l, ExecutionEngine *engine, const Heap::Object *o = static_cast(object.heapObject()); if (o) { if (l->classList[0] == o->internalClass) { - o = o->prototype(); - if (l->classList[1] == o->internalClass) { - o = o->prototype(); + Q_ASSERT(o->prototype() == l->proto); + if (l->classList[1] == l->proto->internalClass) { + o = l->proto->prototype(); if (l->classList[2] == o->internalClass) { Scope scope(o->internalClass->engine); ScopedFunctionObject getter(scope, o->propertyData(l->index + Object::GetterOffset)); @@ -877,7 +877,7 @@ void Lookup::setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, c void Lookup::setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { - Object *o = object.as(); + Object *o = static_cast(object.managed()); if (o && o->internalClass() == l->classList[0]) { *o->propertyData(l->index) = value; return; @@ -888,7 +888,7 @@ void Lookup::setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Va void Lookup::setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { - Object *o = object.as(); + Object *o = static_cast(object.managed()); if (o && o->internalClass() == l->classList[0]) { *o->d()->inlinePropertyData(l->index) = value; return; @@ -899,13 +899,12 @@ void Lookup::setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, co void Lookup::setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { - Object *o = object.as(); + Object *o = static_cast(object.managed()); if (o && o->internalClass() == l->classList[0]) { - if (!o->prototype()) { - o->setInternalClass(l->classList[3]); - *o->propertyData(l->index) = value; - return; - } + Q_ASSERT(!o->prototype()); + o->setInternalClass(l->classList[3]); + *o->propertyData(l->index) = value; + return; } l->setter = setterFallback; @@ -914,10 +913,12 @@ void Lookup::setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, co void Lookup::setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { - Object *o = object.as(); + Object *o = static_cast(object.managed()); if (o && o->internalClass() == l->classList[0]) { Heap::Object *p = o->prototype(); - if (p && p->internalClass == l->classList[1]) { + Q_ASSERT(p); + if (p->internalClass == l->classList[1]) { + Q_ASSERT(!p->prototype()); o->setInternalClass(l->classList[3]); *o->propertyData(l->index) = value; return; @@ -930,12 +931,15 @@ void Lookup::setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, co void Lookup::setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { - Object *o = object.as(); + Object *o = static_cast(object.managed()); if (o && o->internalClass() == l->classList[0]) { Heap::Object *p = o->prototype(); - if (p && p->internalClass == l->classList[1]) { + Q_ASSERT(p); + if (p->internalClass == l->classList[1]) { p = p->prototype(); - if (p && p->internalClass == l->classList[2]) { + Q_ASSERT(p); + if (p->internalClass == l->classList[2]) { + Q_ASSERT(!p->prototype()); o->setInternalClass(l->classList[3]); *o->propertyData(l->index) = value; return; @@ -949,7 +953,7 @@ void Lookup::setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, co void Lookup::setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { - Object *o = object.as(); + Object *o = static_cast(object.managed()); if (o) { if (o->internalClass() == l->classList[0]) { *o->propertyData(l->index) = value; diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index 9e50235b73..151231991f 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -79,13 +79,14 @@ struct Lookup { struct { void *dummy0; void *dummy1; + void *dummy2; Heap::Object *proto; - unsigned type; }; }; union { int level; uint index2; + unsigned type; }; uint index; uint nameIndex; diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 4abe508e10..c3ebb53622 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -505,7 +505,9 @@ ReturnedValue Object::getLookup(const Managed *m, Lookup *l) PropertyAttributes attrs; ReturnedValue v = l->lookup(o, &attrs); if (v != Primitive::emptyValue().asReturnedValue()) { + l->proto = l->classList[0]->prototype; if (attrs.isData()) { + Q_ASSERT(l->classList[0] == o->internalClass()); if (l->level == 0) { uint nInline = o->d()->vtable()->nInlineProperties; if (l->index < nInline) -- cgit v1.2.3 From c03eedaf8c6846dc80ab014face4d514deb9665d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 16 May 2017 15:03:39 +0200 Subject: Optimize lookups in the internalClass Inline the version taking an identifier, and use that one where it makes sense. Change-Id: I414c5999e61cdba219ecd1080957f3037dfebc1b Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4context.cpp | 22 +++++++++++++++++----- src/qml/jsruntime/qv4internalclass.cpp | 9 --------- src/qml/jsruntime/qv4internalclass_p.h | 9 ++++++++- src/qml/jsruntime/qv4object.cpp | 30 ++++++++++++++++++++++-------- 4 files changed, 47 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index c4a0539750..03595aa59d 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -209,6 +209,9 @@ unsigned int CallContext::variableCount() const bool ExecutionContext::deleteProperty(String *name) { + name->makeIdentifier(engine()); + Identifier *id = name->identifier(); + Scope scope(this); bool hasWith = false; ScopedContext ctx(scope, this); @@ -237,7 +240,7 @@ bool ExecutionContext::deleteProperty(String *name) case Heap::ExecutionContext::Type_SimpleCallContext: { Heap::CallContext *c = static_cast(ctx->d()); if (c->v4Function && (c->v4Function->needsActivation() || hasWith)) { - uint index = c->v4Function->internalClass->find(name); + uint index = c->v4Function->internalClass->find(id); if (index < UINT_MAX) // ### throw in strict mode? return false; @@ -361,6 +364,9 @@ void QV4::ExecutionContext::simpleCall(Scope &scope, CallData *callData, Functio void ExecutionContext::setProperty(String *name, const Value &value) { + name->makeIdentifier(engine()); + Identifier *id = name->identifier(); + Scope scope(this); ScopedContext ctx(scope, this); ScopedObject activation(scope); @@ -392,7 +398,7 @@ void ExecutionContext::setProperty(String *name, const Value &value) case Heap::ExecutionContext::Type_SimpleCallContext: { Heap::CallContext *c = static_cast(ctx->d()); if (c->v4Function) { - uint index = c->v4Function->internalClass->find(name); + uint index = c->v4Function->internalClass->find(id); if (index < UINT_MAX) { if (index < c->v4Function->nFormals) { c->callData->args[c->v4Function->nFormals - index - 1] = value; @@ -414,7 +420,7 @@ void ExecutionContext::setProperty(String *name, const Value &value) } if (activation) { - uint member = activation->internalClass()->find(name); + uint member = activation->internalClass()->find(id); if (member < UINT_MAX) { activation->putValue(member, value); return; @@ -473,7 +479,10 @@ ReturnedValue ExecutionContext::getProperty(String *name) case Heap::ExecutionContext::Type_SimpleCallContext: { Heap::CallContext *c = static_cast(ctx->d()); if (c->v4Function && (c->v4Function->needsActivation() || hasWith || hasCatchScope)) { - uint index = c->v4Function->internalClass->find(name); + name->makeIdentifier(engine()); + Identifier *id = name->identifier(); + + uint index = c->v4Function->internalClass->find(id); if (index < UINT_MAX) { if (index < c->v4Function->nFormals) return c->callData->args[c->v4Function->nFormals - index - 1].asReturnedValue(); @@ -551,7 +560,10 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) case Heap::ExecutionContext::Type_SimpleCallContext: { Heap::CallContext *c = static_cast(ctx->d()); if (c->v4Function && (c->v4Function->needsActivation() || hasWith || hasCatchScope)) { - uint index = c->v4Function->internalClass->find(name); + name->makeIdentifier(engine()); + Identifier *id = name->identifier(); + + uint index = c->v4Function->internalClass->find(id); if (index < UINT_MAX) { if (index < c->v4Function->nFormals) return c->callData->args[c->v4Function->nFormals - index - 1].asReturnedValue(); diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index 600198567a..9f43871f27 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -429,15 +429,6 @@ uint QV4::InternalClass::find(const String *string) return UINT_MAX; } -uint InternalClass::find(const Identifier *id) -{ - uint index = propertyTable.lookup(id); - if (index < size) - return index; - - return UINT_MAX; -} - InternalClass *InternalClass::sealed() { if (m_sealed) diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index 031f66793b..34422098d9 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -279,7 +279,14 @@ struct InternalClass : public QQmlJS::Managed { static void changeMember(Object *object, String *string, PropertyAttributes data, uint *index = 0); static void removeMember(Object *object, Identifier *id); uint find(const String *string); - uint find(const Identifier *id); + uint find(const Identifier *id) + { + uint index = propertyTable.lookup(id); + if (index < size) + return index; + + return UINT_MAX; + } Q_REQUIRED_RESULT InternalClass *sealed(); Q_REQUIRED_RESULT InternalClass *frozen(); diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index c3ebb53622..cac9d8ad7d 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -294,7 +294,10 @@ void Object::getOwnProperty(String *name, PropertyAttributes *attrs, Property *p if (idx != UINT_MAX) return getOwnProperty(idx, attrs, p); - uint member = internalClass()->find(name); + name->makeIdentifier(engine()); + Identifier *id = name->identifier(); + + uint member = internalClass()->find(id); if (member < UINT_MAX) { *attrs = internalClass()->propertyData[member]; if (p) { @@ -336,9 +339,12 @@ Value *Object::getValueOrSetter(String *name, PropertyAttributes *attrs) { Q_ASSERT(name->asArrayIndex() == UINT_MAX); + name->makeIdentifier(engine()); + Identifier *id = name->identifier(); + Heap::Object *o = d(); while (o) { - uint idx = o->internalClass->find(name); + uint idx = o->internalClass->find(id); if (idx < UINT_MAX) { *attrs = o->internalClass->propertyData[idx]; return o->propertyData(attrs->isAccessor() ? idx + SetterOffset : idx); @@ -411,7 +417,10 @@ bool Object::hasOwnProperty(String *name) const if (idx != UINT_MAX) return hasOwnProperty(idx); - if (internalClass()->find(name) < UINT_MAX) + name->makeIdentifier(engine()); + Identifier *id = name->identifier(); + + if (internalClass()->find(id) < UINT_MAX) return true; if (!query(name).isEmpty()) return true; @@ -468,8 +477,11 @@ PropertyAttributes Object::query(const Managed *m, String *name) if (idx != UINT_MAX) return queryIndexed(m, idx); + name->makeIdentifier(m->internalClass()->engine); + Identifier *id = name->identifier(); + const Object *o = static_cast(m); - idx = o->internalClass()->find(name); + idx = o->internalClass()->find(id); if (idx < UINT_MAX) return o->internalClass()->propertyData[idx]; @@ -670,10 +682,11 @@ ReturnedValue Object::internalGet(String *name, bool *hasProperty) const Scope scope(engine()); name->makeIdentifier(scope.engine); + Identifier *id = name->identifier(); ScopedObject o(scope, this); while (o) { - uint idx = o->internalClass()->find(name); + uint idx = o->internalClass()->find(id); if (idx < UINT_MAX) { if (hasProperty) *hasProperty = true; @@ -736,8 +749,9 @@ void Object::internalPut(String *name, const Value &value) return putIndexed(idx, value); name->makeIdentifier(engine()); + Identifier *id = name->identifier(); - uint member = internalClass()->find(name); + uint member = internalClass()->find(id); Value *v = 0; PropertyAttributes attrs; if (member < UINT_MAX) { @@ -890,7 +904,7 @@ bool Object::internalDeleteProperty(String *name) name->makeIdentifier(engine()); - uint memberIdx = internalClass()->find(name); + uint memberIdx = internalClass()->find(name->identifier()); if (memberIdx != UINT_MAX) { if (internalClass()->propertyData[memberIdx].isConfigurable()) { InternalClass::removeMember(this, name->identifier()); @@ -961,7 +975,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const } // Clause 1 - memberIndex = internalClass()->find(name); + memberIndex = internalClass()->find(name->identifier()); if (memberIndex == UINT_MAX) { // clause 3 -- cgit v1.2.3 From 8bb01e03e018de7e040b3224c6cdaf43d86051e5 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 16 May 2017 15:10:03 +0200 Subject: Give standard objects a bit more inline storage by default Make sure we have at least 2 slots for inline storage available for regular JS objects. Speeds up JS execution quite a bit, while still keeping memory consumption low for most other cases. Change-Id: I01824d8db1ffd828c1c1b6a9cbcf9bf1a9070ec3 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4managed_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h index 814755efe9..4c387a7fe7 100644 --- a/src/qml/jsruntime/qv4managed_p.h +++ b/src/qml/jsruntime/qv4managed_p.h @@ -131,7 +131,7 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} { \ parentVTable, \ (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \ - (sizeof(classname::Data) + QV4::Chunk::SlotSize - 1)/QV4::Chunk::SlotSize*QV4::Chunk::SlotSize/sizeof(QV4::Value) \ + (sizeof(classname::Data) + (std::is_same::value ? 2*sizeof(QV4::Value) : 0) + QV4::Chunk::SlotSize - 1)/QV4::Chunk::SlotSize*QV4::Chunk::SlotSize/sizeof(QV4::Value) \ - (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \ classname::IsExecutionContext, \ classname::IsString, \ -- cgit v1.2.3 From 32a1d14a3a1dead69dd1306adf70eefd3415639e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 16 May 2017 15:29:17 +0200 Subject: Optimize marking of prototypes in the InternalClass tree There's no need to iterate over all internal classes, as prototype changes always happen in the first or second level of the tree. Change-Id: I99bf11a6cd238286c1547922d61ab47319b6eb97 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4internalclass.cpp | 32 ++++++++++++++++---------------- src/qml/jsruntime/qv4internalclass_p.h | 1 - 2 files changed, 16 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index 9f43871f27..0bcd510541 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -506,25 +506,25 @@ void InternalClass::destroy() } } -void InternalClass::mark(ExecutionEngine *e) -{ - if (m_sealed) - m_sealed->mark(e); - if (m_frozen) - m_frozen->mark(e); - - for (size_t i = 0; i < transitions.size(); ++i) { - Q_ASSERT(transitions.at(i).lookup); - transitions.at(i).lookup->mark(e); - } - if (prototype) - prototype->mark(engine); -} - void InternalClassPool::markObjects(ExecutionEngine *engine) { InternalClass *ic = engine->internalClasses[EngineBase::Class_Empty]; - ic->mark(engine); + Q_ASSERT(!ic->prototype); + + // only need to go two levels into the IC hierarchy, as prototype changes + // can only happen there + for (auto &t : ic->transitions) { + Q_ASSERT(t.lookup); + if (t.flags == InternalClassTransition::VTableChange) { + InternalClass *ic2 = t.lookup; + for (auto &t2 : ic2->transitions) { + if (t2.flags == InternalClassTransition::PrototypeChange) + t2.lookup->prototype->mark(engine); + } + } else if (t.flags == InternalClassTransition::PrototypeChange) { + t.lookup->prototype->mark(engine); + } + } } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index 34422098d9..9e8ab9e73e 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -293,7 +293,6 @@ struct InternalClass : public QQmlJS::Managed { Q_REQUIRED_RESULT InternalClass *propertiesFrozen() const; void destroy(); - void mark(ExecutionEngine *e); private: Q_QML_EXPORT InternalClass *changeVTableImpl(const VTable *vt); -- cgit v1.2.3 From e6c0595b2138929f5cb6199cde9c3d79d549e0ae Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Mon, 2 Jan 2017 12:35:35 +0100 Subject: Add advance property to QQuickText This is a step on the way to certain optimization possibilities for text in Qt Quick. Basically, to allow users the ability to split text into separate items (so that you can distinguish dynamic from static, and unshaped from shaped text), we also need a way to string those items together visually later. The advance property is required for this. [ChangeLog][QtQuick][Text] Added "advance" property to Text element. Task-number: QTBUG-56728 Change-Id: I8e7bf9bac410fa9c5553b48db90956431a2873f6 Reviewed-by: Simon Hausmann --- src/quick/items/qquicktext.cpp | 47 ++++++++++++++++++++++++++++++++++++++++ src/quick/items/qquicktext_p.h | 2 ++ src/quick/items/qquicktext_p_p.h | 1 + 3 files changed, 50 insertions(+) (limited to 'src') diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 09371db66d..080cc9412e 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -384,6 +384,7 @@ void QQuickTextPrivate::updateSize() updateBaseline(fm.ascent(), q->height() - fontHeight - vPadding); q->setImplicitSize(hPadding, fontHeight + vPadding); layedOutTextRect = QRectF(0, 0, 0, fontHeight); + advance = QSizeF(); emit q->contentSizeChanged(); updateType = UpdatePaintNode; q->update(); @@ -457,8 +458,26 @@ void QQuickTextPrivate::updateSize() if (iWidth == -1) q->setImplicitHeight(size.height() + vPadding); + + QTextBlock firstBlock = extra->doc->firstBlock(); + while (firstBlock.layout()->lineCount() == 0) + firstBlock = firstBlock.next(); + + QTextBlock lastBlock = extra->doc->lastBlock(); + while (lastBlock.layout()->lineCount() == 0) + lastBlock = lastBlock.previous(); + + if (firstBlock.lineCount() > 0 && lastBlock.lineCount() > 0) { + QTextLine firstLine = firstBlock.layout()->lineAt(0); + QTextLine lastLine = lastBlock.layout()->lineAt(lastBlock.layout()->lineCount() - 1); + advance = QSizeF(lastLine.horizontalAdvance(), + (lastLine.y() + lastBlock.layout()->position().y()) - (firstLine.y() + firstBlock.layout()->position().y())); + } else { + advance = QSizeF(); + } } + if (layedOutTextRect.size() != previousSize) emit q->contentSizeChanged(); updateType = UpdatePaintNode; @@ -968,6 +987,16 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline) br.moveTop(0); + // Find the advance of the text layout + if (layout.lineCount() > 0) { + QTextLine firstLine = layout.lineAt(0); + QTextLine lastLine = layout.lineAt(layout.lineCount() - 1); + advance = QSizeF(lastLine.horizontalAdvance(), + lastLine.y() - firstLine.y()); + } else { + advance = QSizeF(); + } + if (!horizontalFit && !verticalFit) break; @@ -3055,6 +3084,24 @@ QJSValue QQuickText::fontInfo() const return value; } +/*! + \qmlproperty size QtQuick::Text::advance + \since 5.10 + + The distance, in pixels, from the baseline origin of the first + character of the text item, to the baseline origin of the first + character in a text item occurring directly after this one + in a text flow. + + Note that the advance can be negative if the text flows from + the right to the left. +*/ +QSizeF QQuickText::advance() const +{ + Q_D(const QQuickText); + return d->advance; +} + QT_END_NAMESPACE #include "moc_qquicktext_p.cpp" diff --git a/src/quick/items/qquicktext_p.h b/src/quick/items/qquicktext_p.h index b190738cfb..a56bcdb87b 100644 --- a/src/quick/items/qquicktext_p.h +++ b/src/quick/items/qquicktext_p.h @@ -99,6 +99,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickText : public QQuickImplicitSizeItem Q_PROPERTY(qreal bottomPadding READ bottomPadding WRITE setBottomPadding RESET resetBottomPadding NOTIFY bottomPaddingChanged REVISION 6) Q_PROPERTY(QJSValue fontInfo READ fontInfo NOTIFY fontInfoChanged REVISION 9) + Q_PROPERTY(QSizeF advance READ advance NOTIFY contentSizeChanged REVISION 10) public: QQuickText(QQuickItem *parent=0); @@ -251,6 +252,7 @@ public: void resetBottomPadding(); QJSValue fontInfo() const; + QSizeF advance() const; Q_SIGNALS: void textChanged(const QString &text); diff --git a/src/quick/items/qquicktext_p_p.h b/src/quick/items/qquicktext_p_p.h index 6456750359..fde07eaf2e 100644 --- a/src/quick/items/qquicktext_p_p.h +++ b/src/quick/items/qquicktext_p_p.h @@ -89,6 +89,7 @@ public: void processHoverEvent(QHoverEvent *event); QRectF layedOutTextRect; + QSizeF advance; struct ExtraData { ExtraData(); -- cgit v1.2.3 From 6a02fb09af8dce6ca533e816d2223e070b30f294 Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Tue, 16 May 2017 19:08:28 +0200 Subject: QQmlIRBuilder: Only query type name cache for type names The behavior here was always incorrect: type names must start with an uppercase letter, so querying the type name cache with a lowercase string is wrong. However, this was turned into a larger problem by making more extensive use of QQmlTypeNameCache in e74a1d0b342f2c95dc3a543c8c9ec07fd52d8fe0, as it contained a lot of new types (including composite types, which previously were only in the cache if they were singletons). Task-number: QTBUG-60547 Change-Id: I40be2d535e99d3e1af250d995d7149ecbe2965d7 Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlirbuilder.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 9f2c2294ed..16eee50cf9 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1924,7 +1924,7 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int // with the correct QML context. // Look for IDs first. - for (const IdMapping &mapping : qAsConst(_idObjects)) + for (const IdMapping &mapping : qAsConst(_idObjects)) { if (name == mapping.name) { if (_function->isQmlBinding) _function->idObjectDependencies.insert(mapping.idIndex); @@ -1942,8 +1942,9 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int result->isReadOnly = true; // don't allow use as lvalue return result; } + } - { + if (name.at(0).isUpper()) { QQmlTypeNameCache::Result r = imports->query(name); if (r.isValid()) { if (r.scriptIndex != -1) { -- cgit v1.2.3 From 5f49b2b3bb30678a26341e1f45e0cfd6a6fd2c57 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 11 May 2017 17:19:14 +0200 Subject: QML Settings: fix JS array handling Before Qt 5.4, JS arrays were passed as QVariantLists. Since Qt 5.4, they are passed as QJSValues instead. Use QJSValue::toVariant() (the same way as QQuickItemView::setModel(QVariant) which was fixed in cf959b4b) to convert JS values to QSettings-compatible variants. Task-number: QTBUG-45316 Change-Id: Icc6f8ad09bfef089d9efcf5b90e3783bb3f73a9f Reviewed-by: Simon Hausmann --- src/imports/settings/qqmlsettings.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/imports/settings/qqmlsettings.cpp b/src/imports/settings/qqmlsettings.cpp index cd6fcbc718..df67c04654 100644 --- a/src/imports/settings/qqmlsettings.cpp +++ b/src/imports/settings/qqmlsettings.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -241,6 +242,7 @@ public: void store(); void _q_propertyChanged(); + QVariant readProperty(const QMetaProperty &property) const; QQmlSettings *q_ptr; int timerId; @@ -295,7 +297,7 @@ void QQmlSettingsPrivate::load() for (int i = offset; i < count; ++i) { QMetaProperty property = mo->property(i); - const QVariant previousValue = property.read(q); + const QVariant previousValue = readProperty(property); const QVariant currentValue = instance()->value(property.name(), previousValue); if (!currentValue.isNull() && (!previousValue.isValid() @@ -340,9 +342,10 @@ void QQmlSettingsPrivate::_q_propertyChanged() const int count = mo->propertyCount(); for (int i = offset; i < count; ++i) { const QMetaProperty &property = mo->property(i); - changedProperties.insert(property.name(), property.read(q)); + const QVariant value = readProperty(property); + changedProperties.insert(property.name(), value); #ifdef SETTINGS_DEBUG - qDebug() << "QQmlSettings: cache" << property.name() << ":" << property.read(q); + qDebug() << "QQmlSettings: cache" << property.name() << ":" << value; #endif } if (timerId != 0) @@ -350,6 +353,15 @@ void QQmlSettingsPrivate::_q_propertyChanged() timerId = q->startTimer(settingsWriteDelay); } +QVariant QQmlSettingsPrivate::readProperty(const QMetaProperty &property) const +{ + Q_Q(const QQmlSettings); + QVariant var = property.read(q); + if (var.userType() == qMetaTypeId()) + var = var.value().toVariant(); + return var; +} + QQmlSettings::QQmlSettings(QObject *parent) : QObject(parent), d_ptr(new QQmlSettingsPrivate) { -- cgit v1.2.3 From 5bc38a50308665bdc185eb96dbcc9ba7948ab4e0 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 19 May 2017 12:39:52 +0200 Subject: Get rid of the old way of defining builtin functions The old calling convention used for builtin functions is very inefficient. It was still being used in a few places. Clean those up and convert them to the new and much more effiecient calling convention. Change-Id: I6b769c6185df7e9be1e80709330fc1ca868576c1 Reviewed-by: Robin Burchell --- src/imports/localstorage/plugin.cpp | 97 +++++++++++++---------------- src/particles/qquickv4particledata.cpp | 107 ++++++++++++++------------------ src/qml/jsruntime/qv4engine.cpp | 4 +- src/qml/jsruntime/qv4functionobject.cpp | 36 ----------- src/qml/jsruntime/qv4functionobject_p.h | 11 ---- src/qml/jsruntime/qv4object.cpp | 40 ------------ src/qml/jsruntime/qv4object_p.h | 4 -- src/qml/util/qqmladaptormodel.cpp | 44 ++++++------- 8 files changed, 114 insertions(+), 229 deletions(-) (limited to 'src') diff --git a/src/imports/localstorage/plugin.cpp b/src/imports/localstorage/plugin.cpp index 40682dd6a8..a7d95cc295 100644 --- a/src/imports/localstorage/plugin.cpp +++ b/src/imports/localstorage/plugin.cpp @@ -73,8 +73,8 @@ QT_BEGIN_NAMESPACE QV4::ScopedString v(scope, scope.engine->newString(desc)); \ QV4::ScopedObject ex(scope, scope.engine->newErrorObject(v)); \ ex->put(QV4::ScopedString(scope, scope.engine->newIdentifier(QStringLiteral("code"))).getPointer(), QV4::ScopedValue(scope, Primitive::fromInt32(error))); \ - ctx->engine()->throwError(ex); \ - return Encode::undefined(); \ + scope.engine->throwError(ex); \ + RETURN_UNDEFINED(); \ } #define V4THROW_SQL2(error, desc) { \ @@ -87,8 +87,8 @@ QT_BEGIN_NAMESPACE #define V4THROW_REFERENCE(string) { \ QV4::ScopedString v(scope, scope.engine->newString(string)); \ - ctx->engine()->throwReferenceError(v); \ - return Encode::undefined(); \ + scope.engine->throwReferenceError(v); \ + RETURN_UNDEFINED(); \ } @@ -164,20 +164,18 @@ DEFINE_OBJECT_VTABLE(QV4::QQmlSqlDatabaseWrapper); -static ReturnedValue qmlsqldatabase_version(CallContext *ctx) +static void qmlsqldatabase_version(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped r(scope, ctx->thisObject().as()); + QV4::Scoped r(scope, callData->thisObject.as()); if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Database) V4THROW_REFERENCE("Not a SQLDatabase object"); - return Encode(scope.engine->newString(*r->d()->version)); + RETURN_RESULT(Encode(scope.engine->newString(*r->d()->version))); } -static ReturnedValue qmlsqldatabase_rows_length(CallContext *ctx) +static void qmlsqldatabase_rows_length(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped r(scope, ctx->thisObject().as()); + QV4::Scoped r(scope, callData->thisObject.as()); if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Rows) V4THROW_REFERENCE("Not a SQLDatabase::Rows object"); @@ -190,29 +188,27 @@ static ReturnedValue qmlsqldatabase_rows_length(CallContext *ctx) s = 0; } } - return Encode(s); + RETURN_RESULT(Encode(s)); } -static ReturnedValue qmlsqldatabase_rows_forwardOnly(CallContext *ctx) +static void qmlsqldatabase_rows_forwardOnly(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped r(scope, ctx->thisObject().as()); + QV4::Scoped r(scope, callData->thisObject.as()); if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Rows) V4THROW_REFERENCE("Not a SQLDatabase::Rows object"); - return Encode(r->d()->sqlQuery->isForwardOnly()); + RETURN_RESULT(Encode(r->d()->sqlQuery->isForwardOnly())); } -static ReturnedValue qmlsqldatabase_rows_setForwardOnly(CallContext *ctx) +static void qmlsqldatabase_rows_setForwardOnly(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped r(scope, ctx->thisObject().as()); + QV4::Scoped r(scope, callData->thisObject.as()); if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Rows) V4THROW_REFERENCE("Not a SQLDatabase::Rows object"); - if (ctx->argc() < 1) - return ctx->engine()->throwTypeError(); + if (callData->argc < 1) + RETURN_RESULT(scope.engine->throwTypeError()); - r->d()->sqlQuery->setForwardOnly(ctx->args()[0].toBoolean()); - return Encode::undefined(); + r->d()->sqlQuery->setForwardOnly(callData->args[0].toBoolean()); + RETURN_UNDEFINED(); } QQmlSqlDatabaseData::~QQmlSqlDatabaseData() @@ -253,14 +249,13 @@ ReturnedValue QQmlSqlDatabaseWrapper::getIndexed(const Managed *m, uint index, b return qmlsqldatabase_rows_index(r, r->engine(), index, hasProperty); } -static ReturnedValue qmlsqldatabase_rows_item(CallContext *ctx) +static void qmlsqldatabase_rows_item(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped r(scope, ctx->thisObject().as()); + QV4::Scoped r(scope, callData->thisObject.as()); if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Rows) V4THROW_REFERENCE("Not a SQLDatabase::Rows object"); - return qmlsqldatabase_rows_index(r, scope.engine, ctx->argc() ? ctx->args()[0].toUInt32() : 0); + RETURN_RESULT(qmlsqldatabase_rows_index(r, scope.engine, callData->argc ? callData->args[0].toUInt32() : 0)); } static QVariant toSqlVariant(QV4::ExecutionEngine *engine, const QV4::ScopedValue &value) @@ -272,10 +267,9 @@ static QVariant toSqlVariant(QV4::ExecutionEngine *engine, const QV4::ScopedValu return engine->toVariant(value, /*typehint*/-1); } -static ReturnedValue qmlsqldatabase_executeSql(CallContext *ctx) +static void qmlsqldatabase_executeSql(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped r(scope, ctx->thisObject().as()); + QV4::Scoped r(scope, callData->thisObject.as()); if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Query) V4THROW_REFERENCE("Not a SQLDatabase::Query object"); @@ -284,7 +278,7 @@ static ReturnedValue qmlsqldatabase_executeSql(CallContext *ctx) QSqlDatabase db = *r->d()->database; - QString sql = ctx->argc() ? ctx->args()[0].toQString() : QString(); + QString sql = callData->argc ? callData->args[0].toQString() : QString(); if (r->d()->readonly && !sql.startsWith(QLatin1String("SELECT"),Qt::CaseInsensitive)) { V4THROW_SQL(SQLEXCEPTION_SYNTAX_ERR, QQmlEngine::tr("Read-only Transaction")); @@ -296,8 +290,8 @@ static ReturnedValue qmlsqldatabase_executeSql(CallContext *ctx) ScopedValue result(scope, Primitive::undefinedValue()); if (query.prepare(sql)) { - if (ctx->argc() > 1) { - ScopedValue values(scope, ctx->args()[1]); + if (callData->argc > 1) { + ScopedValue values(scope, callData->args[1]); if (values->as()) { ScopedArrayObject array(scope, values); quint32 size = array->getLength(); @@ -351,7 +345,7 @@ static ReturnedValue qmlsqldatabase_executeSql(CallContext *ctx) if (err) V4THROW_SQL(SQLEXCEPTION_DATABASE_ERR,query.lastError().text()); - return result->asReturnedValue(); + RETURN_RESULT(result->asReturnedValue()); } struct TransactionRollback { @@ -383,21 +377,19 @@ struct TransactionRollback { }; -static ReturnedValue qmlsqldatabase_changeVersion(CallContext *ctx) +static void qmlsqldatabase_changeVersion(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - if (ctx->argc() < 2) - return Encode::undefined(); - - Scope scope(ctx); + if (callData->argc < 2) + RETURN_UNDEFINED(); - Scoped r(scope, ctx->thisObject()); + Scoped r(scope, callData->thisObject); if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Database) V4THROW_REFERENCE("Not a SQLDatabase object"); QSqlDatabase db = *r->d()->database; - QString from_version = ctx->args()[0].toQString(); - QString to_version = ctx->args()[1].toQString(); - ScopedFunctionObject callback(scope, ctx->argument(2)); + QString from_version = callData->args[0].toQString(); + QString to_version = callData->args[1].toQString(); + ScopedFunctionObject callback(scope, callData->argc > 2 ? callData->args[2] : Primitive::undefinedValue()); if (from_version != *r->d()->version) V4THROW_SQL(SQLEXCEPTION_VERSION_ERR, QQmlEngine::tr("Version mismatch: expected %1, found %2").arg(from_version).arg(*r->d()->version)); @@ -438,17 +430,16 @@ static ReturnedValue qmlsqldatabase_changeVersion(CallContext *ctx) #endif } - return Encode::undefined(); + RETURN_UNDEFINED(); } -static ReturnedValue qmlsqldatabase_transaction_shared(CallContext *ctx, bool readOnly) +static void qmlsqldatabase_transaction_shared(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData, bool readOnly) { - QV4::Scope scope(ctx); - QV4::Scoped r(scope, ctx->thisObject().as()); + QV4::Scoped r(scope, callData->thisObject.as()); if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Database) V4THROW_REFERENCE("Not a SQLDatabase object"); - const FunctionObject *callback = ctx->argc() ? ctx->args()[0].as() : 0; + const FunctionObject *callback = callData->argc ? callData->args[0].as() : 0; if (!callback) V4THROW_SQL(SQLEXCEPTION_UNKNOWN_ERR, QQmlEngine::tr("transaction: missing callback")); @@ -475,17 +466,17 @@ static ReturnedValue qmlsqldatabase_transaction_shared(CallContext *ctx, bool re db.rollback(); } - return Encode::undefined(); + RETURN_UNDEFINED(); } -static ReturnedValue qmlsqldatabase_transaction(CallContext *ctx) +static void qmlsqldatabase_transaction(const QV4::BuiltinFunction *f, QV4::Scope &scope, QV4::CallData *callData) { - return qmlsqldatabase_transaction_shared(ctx, false); + qmlsqldatabase_transaction_shared(f, scope, callData, false); } -static ReturnedValue qmlsqldatabase_read_transaction(CallContext *ctx) +static void qmlsqldatabase_read_transaction(const QV4::BuiltinFunction *f, QV4::Scope &scope, QV4::CallData *callData) { - return qmlsqldatabase_transaction_shared(ctx, true); + qmlsqldatabase_transaction_shared(f, scope, callData, true); } QQmlSqlDatabaseData::QQmlSqlDatabaseData(ExecutionEngine *v4) diff --git a/src/particles/qquickv4particledata.cpp b/src/particles/qquickv4particledata.cpp index 967652f31a..e8376f1c27 100644 --- a/src/particles/qquickv4particledata.cpp +++ b/src/particles/qquickv4particledata.cpp @@ -296,123 +296,112 @@ public: QV4::PersistentValue proto; }; -static QV4::ReturnedValue particleData_discard(QV4::CallContext *ctx) +static void particleData_discard(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped r(scope, ctx->thisObject()); + QV4::Scoped r(scope, callData->thisObject); if (!r || !r->d()->datum) - return ctx->engine()->throwError(QStringLiteral("Not a valid ParticleData object")); + RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object"))); r->d()->datum->lifeSpan = 0; //Don't kill(), because it could still be in the middle of being created - return QV4::Encode::undefined(); + RETURN_RESULT(QV4::Encode::undefined()); } -static QV4::ReturnedValue particleData_lifeLeft(QV4::CallContext *ctx) +static void particleData_lifeLeft(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped r(scope, ctx->thisObject()); + QV4::Scoped r(scope, callData->thisObject); if (!r || !r->d()->datum) - return ctx->engine()->throwError(QStringLiteral("Not a valid ParticleData object")); + RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object"))); - return QV4::Encode(r->d()->datum->lifeLeft(r->d()->particleSystem)); + RETURN_RESULT(QV4::Encode(r->d()->datum->lifeLeft(r->d()->particleSystem))); } -static QV4::ReturnedValue particleData_curSize(QV4::CallContext *ctx) +static void particleData_curSize(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped r(scope, ctx->thisObject()); + QV4::Scoped r(scope, callData->thisObject); if (!r || !r->d()->datum) - return ctx->engine()->throwError(QStringLiteral("Not a valid ParticleData object")); + RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object"))); - return QV4::Encode(r->d()->datum->curSize(r->d()->particleSystem)); + RETURN_RESULT(QV4::Encode(r->d()->datum->curSize(r->d()->particleSystem))); } -#define COLOR_GETTER_AND_SETTER(VAR, NAME) static QV4::ReturnedValue particleData_get_ ## NAME (QV4::CallContext *ctx) \ +#define COLOR_GETTER_AND_SETTER(VAR, NAME) static void particleData_get_ ## NAME (const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) \ { \ - QV4::Scope scope(ctx); \ - QV4::Scoped r(scope, ctx->thisObject()); \ + QV4::Scoped r(scope, callData->thisObject); \ if (!r || !r->d()->datum) \ - ctx->engine()->throwError(QStringLiteral("Not a valid ParticleData object")); \ + RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object"))); \ \ - return QV4::Encode((r->d()->datum->color. VAR )/255.0);\ + RETURN_RESULT(QV4::Encode((r->d()->datum->color. VAR )/255.0));\ }\ \ -static QV4::ReturnedValue particleData_set_ ## NAME (QV4::CallContext *ctx)\ +static void particleData_set_ ## NAME (const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)\ {\ - QV4::Scope scope(ctx); \ - QV4::Scoped r(scope, ctx->thisObject()); \ + QV4::Scoped r(scope, callData->thisObject); \ if (!r || !r->d()->datum)\ - ctx->engine()->throwError(QStringLiteral("Not a valid ParticleData object"));\ + RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object")));\ \ - double d = ctx->argc() ? ctx->args()[0].toNumber() : 0; \ + double d = callData->argc ? callData->args[0].toNumber() : 0; \ r->d()->datum->color. VAR = qMin(255, qMax(0, (int)::floor(d * 255.0)));\ - return QV4::Encode::undefined(); \ + RETURN_UNDEFINED(); \ } -#define SEMIBOOL_GETTER_AND_SETTER(VARIABLE) static QV4::ReturnedValue particleData_get_ ## VARIABLE (QV4::CallContext *ctx) \ +#define SEMIBOOL_GETTER_AND_SETTER(VARIABLE) static void particleData_get_ ## VARIABLE (const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) \ { \ - QV4::Scope scope(ctx); \ - QV4::Scoped r(scope, ctx->thisObject()); \ + QV4::Scoped r(scope, callData->thisObject); \ if (!r || !r->d()->datum) \ - ctx->engine()->throwError(QStringLiteral("Not a valid ParticleData object")); \ + RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object"))); \ \ - return QV4::Encode(r->d()->datum-> VARIABLE);\ + RETURN_RESULT(QV4::Encode(r->d()->datum-> VARIABLE));\ }\ \ -static QV4::ReturnedValue particleData_set_ ## VARIABLE (QV4::CallContext *ctx)\ +static void particleData_set_ ## VARIABLE (const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)\ {\ - QV4::Scope scope(ctx); \ - QV4::Scoped r(scope, ctx->thisObject()); \ + QV4::Scoped r(scope, callData->thisObject); \ if (!r || !r->d()->datum)\ - ctx->engine()->throwError(QStringLiteral("Not a valid ParticleData object"));\ + RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object")));\ \ - r->d()->datum-> VARIABLE = (ctx->argc() && ctx->args()[0].toBoolean()) ? 1.0 : 0.0;\ - return QV4::Encode::undefined(); \ + r->d()->datum-> VARIABLE = (callData->argc && callData->args[0].toBoolean()) ? 1.0 : 0.0;\ + RETURN_UNDEFINED(); \ } -#define FLOAT_GETTER_AND_SETTER(VARIABLE) static QV4::ReturnedValue particleData_get_ ## VARIABLE (QV4::CallContext *ctx) \ +#define FLOAT_GETTER_AND_SETTER(VARIABLE) static void particleData_get_ ## VARIABLE (const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) \ { \ - QV4::Scope scope(ctx); \ - QV4::Scoped r(scope, ctx->thisObject()); \ + QV4::Scoped r(scope, callData->thisObject); \ if (!r || !r->d()->datum) \ - ctx->engine()->throwError(QStringLiteral("Not a valid ParticleData object")); \ + RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object"))); \ \ - return QV4::Encode(r->d()->datum-> VARIABLE);\ + RETURN_RESULT(QV4::Encode(r->d()->datum-> VARIABLE));\ }\ \ -static QV4::ReturnedValue particleData_set_ ## VARIABLE (QV4::CallContext *ctx)\ +static void particleData_set_ ## VARIABLE (const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)\ {\ - QV4::Scope scope(ctx); \ - QV4::Scoped r(scope, ctx->thisObject()); \ + QV4::Scoped r(scope, callData->thisObject); \ if (!r || !r->d()->datum)\ - ctx->engine()->throwError(QStringLiteral("Not a valid ParticleData object"));\ + RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object")));\ \ - r->d()->datum-> VARIABLE = ctx->argc() ? ctx->args()[0].toNumber() : qt_qnan();\ - return QV4::Encode::undefined(); \ + r->d()->datum-> VARIABLE = callData->argc ? callData->args[0].toNumber() : qt_qnan();\ + RETURN_UNDEFINED(); \ } -#define FAKE_FLOAT_GETTER_AND_SETTER(VARIABLE, GETTER, SETTER) static QV4::ReturnedValue particleData_get_ ## VARIABLE (QV4::CallContext *ctx) \ +#define FAKE_FLOAT_GETTER_AND_SETTER(VARIABLE, GETTER, SETTER) static void particleData_get_ ## VARIABLE (const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) \ { \ - QV4::Scope scope(ctx); \ - QV4::Scoped r(scope, ctx->thisObject()); \ + QV4::Scoped r(scope, callData->thisObject); \ if (!r || !r->d()->datum) \ - ctx->engine()->throwError(QStringLiteral("Not a valid ParticleData object")); \ + RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object"))); \ \ - return QV4::Encode(r->d()->datum-> GETTER (r->d()->particleSystem));\ + RETURN_RESULT(QV4::Encode(r->d()->datum-> GETTER (r->d()->particleSystem)));\ }\ \ -static QV4::ReturnedValue particleData_set_ ## VARIABLE (QV4::CallContext *ctx)\ +static void particleData_set_ ## VARIABLE (const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)\ {\ - QV4::Scope scope(ctx); \ - QV4::Scoped r(scope, ctx->thisObject()); \ + QV4::Scoped r(scope, callData->thisObject); \ if (!r || !r->d()->datum)\ - ctx->engine()->throwError(QStringLiteral("Not a valid ParticleData object"));\ + RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object")));\ \ - r->d()->datum-> SETTER (ctx->argc() ? ctx->args()[0].toNumber() : qt_qnan(), r->d()->particleSystem);\ - return QV4::Encode::undefined(); \ + r->d()->datum-> SETTER (callData->argc ? callData->args[0].toNumber() : qt_qnan(), r->d()->particleSystem);\ + RETURN_UNDEFINED(); \ } #define REGISTER_ACCESSOR(PROTO, ENGINE, VARIABLE, NAME) \ diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index daab7ad279..cb666451bb 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -109,9 +109,9 @@ using namespace QV4; static QBasicAtomicInt engineSerial = Q_BASIC_ATOMIC_INITIALIZER(1); -static ReturnedValue throwTypeError(CallContext *ctx) +void throwTypeError(const BuiltinFunction *, Scope &scope, CallData *) { - return ctx->engine()->throwTypeError(); + scope.result = scope.engine->throwTypeError(); } diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 4be14c09ba..bd6eb17bb4 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -460,42 +460,6 @@ InternalClass *ScriptFunction::classForConstructor() const } - -DEFINE_OBJECT_VTABLE(OldBuiltinFunction); - -void Heap::OldBuiltinFunction::init(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(QV4::CallContext *)) -{ - Heap::FunctionObject::init(scope, name); - this->code = code; -} - -void OldBuiltinFunction::construct(const Managed *f, Scope &scope, CallData *) -{ - scope.result = static_cast(f)->internalClass()->engine->throwTypeError(); -} - -void OldBuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData) -{ - const OldBuiltinFunction *f = static_cast(that); - ExecutionEngine *v4 = scope.engine; - if (v4->hasException) { - scope.result = Encode::undefined(); - return; - } - CHECK_STACK_LIMITS(v4, scope); - - ExecutionContextSaver ctxSaver(scope); - - CallContext::Data *ctx = v4->memoryManager->allocSimpleCallContext(v4); - ctx->strictMode = f->scope()->strictMode; // ### needed? scope or parent context? - ctx->callData = callData; - v4->pushContext(ctx); - Q_ASSERT(v4->current == ctx); - - scope.result = f->d()->code(static_cast(v4->currentContext)); - v4->memoryManager->freeSimpleCallContext(); -} - DEFINE_OBJECT_VTABLE(BuiltinFunction); void Heap::BuiltinFunction::init(QV4::ExecutionContext *scope, QV4::String *name, void (*code)(const QV4::BuiltinFunction *, Scope &, CallData *)) diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index 354f6b2e3f..d691b869fe 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -192,21 +192,10 @@ struct FunctionPrototype: FunctionObject static void method_bind(const BuiltinFunction *, Scope &scope, CallData *callData); }; -struct Q_QML_EXPORT OldBuiltinFunction : FunctionObject { - V4_OBJECT2(OldBuiltinFunction, FunctionObject) - - static void construct(const Managed *, Scope &scope, CallData *); - static void call(const Managed *that, Scope &scope, CallData *callData); -}; - struct Q_QML_EXPORT BuiltinFunction : FunctionObject { V4_OBJECT2(BuiltinFunction, FunctionObject) V4_INTERNALCLASS(BuiltinFunction) - static Heap::OldBuiltinFunction *create(ExecutionContext *scope, String *name, ReturnedValue (*code)(CallContext *)) - { - return scope->engine()->memoryManager->allocObject(scope, name, code); - } static Heap::BuiltinFunction *create(ExecutionContext *scope, String *name, void (*code)(const BuiltinFunction *, Scope &, CallData *)) { return scope->engine()->memoryManager->allocObject(scope, name, code); diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index cac9d8ad7d..04336d4f88 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -162,17 +162,6 @@ void Object::defineDefaultProperty(const QString &name, const Value &value) defineDefaultProperty(s, value); } -void Object::defineDefaultProperty(const QString &name, ReturnedValue (*code)(CallContext *), int argumentCount) -{ - ExecutionEngine *e = engine(); - Scope scope(e); - ScopedString s(scope, e->newIdentifier(name)); - ExecutionContext *global = e->rootContext(); - ScopedFunctionObject function(scope, BuiltinFunction::create(global, s, code)); - function->defineReadonlyProperty(e->id_length(), Primitive::fromInt32(argumentCount)); - defineDefaultProperty(s, function); -} - void Object::defineDefaultProperty(const QString &name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount) { ExecutionEngine *e = engine(); @@ -184,16 +173,6 @@ void Object::defineDefaultProperty(const QString &name, void (*code)(const Built defineDefaultProperty(s, function); } -void Object::defineDefaultProperty(String *name, ReturnedValue (*code)(CallContext *), int argumentCount) -{ - ExecutionEngine *e = engine(); - Scope scope(e); - ExecutionContext *global = e->rootContext(); - ScopedFunctionObject function(scope, BuiltinFunction::create(global, name, code)); - function->defineReadonlyProperty(e->id_length(), Primitive::fromInt32(argumentCount)); - defineDefaultProperty(name, function); -} - void Object::defineDefaultProperty(String *name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount) { ExecutionEngine *e = engine(); @@ -204,25 +183,6 @@ void Object::defineDefaultProperty(String *name, void (*code)(const BuiltinFunct defineDefaultProperty(name, function); } -void Object::defineAccessorProperty(const QString &name, ReturnedValue (*getter)(CallContext *), ReturnedValue (*setter)(CallContext *)) -{ - ExecutionEngine *e = engine(); - Scope scope(e); - ScopedString s(scope, e->newIdentifier(name)); - defineAccessorProperty(s, getter, setter); -} - -void Object::defineAccessorProperty(String *name, ReturnedValue (*getter)(CallContext *), ReturnedValue (*setter)(CallContext *)) -{ - ExecutionEngine *v4 = engine(); - QV4::Scope scope(v4); - ScopedProperty p(scope); - ExecutionContext *global = v4->rootContext(); - p->setGetter(ScopedFunctionObject(scope, (getter ? BuiltinFunction::create(global, name, getter) : 0))); - p->setSetter(ScopedFunctionObject(scope, (setter ? BuiltinFunction::create(global, name, setter) : 0))); - insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); -} - void Object::defineAccessorProperty(const QString &name, void (*getter)(const BuiltinFunction *, Scope &, CallData *), void (*setter)(const BuiltinFunction *, Scope &, CallData *)) { diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index cf04a84175..a9afe14129 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -255,12 +255,8 @@ struct Q_QML_EXPORT Object: Managed { insertMember(name, value, Attr_Data|Attr_NotEnumerable); } void defineDefaultProperty(const QString &name, const Value &value); - void defineDefaultProperty(const QString &name, ReturnedValue (*code)(CallContext *), int argumentCount = 0); void defineDefaultProperty(const QString &name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount = 0); - void defineDefaultProperty(String *name, ReturnedValue (*code)(CallContext *), int argumentCount = 0); void defineDefaultProperty(String *name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount = 0); - void defineAccessorProperty(const QString &name, ReturnedValue (*getter)(CallContext *), ReturnedValue (*setter)(CallContext *)); - void defineAccessorProperty(String *name, ReturnedValue (*getter)(CallContext *), ReturnedValue (*setter)(CallContext *)); void defineAccessorProperty(const QString &name, void (*getter)(const BuiltinFunction *, Scope &, CallData *), void (*setter)(const BuiltinFunction *, Scope &, CallData *)); void defineAccessorProperty(String *name, void (*getter)(const BuiltinFunction *, Scope &, CallData *), diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp index b9d312d41f..d4aa2e19ed 100644 --- a/src/qml/util/qqmladaptormodel.cpp +++ b/src/qml/util/qqmladaptormodel.cpp @@ -61,14 +61,13 @@ public: V4_DEFINE_EXTENSION(QQmlAdaptorModelEngineData, engineData) -static QV4::ReturnedValue get_index(QV4::CallContext *ctx) +static void get_index(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped o(scope, ctx->thisObject().as()); + QV4::Scoped o(scope, callData->thisObject.as()); if (!o) - return ctx->engine()->throwTypeError(QStringLiteral("Not a valid VisualData object")); + RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"))); - return QV4::Encode(o->d()->item->index); + RETURN_RESULT(QV4::Encode(o->d()->item->index)); } template static void setModelDataType(QMetaObjectBuilder *builder, M *metaType) @@ -195,19 +194,18 @@ public: dataType->watchedRoles += newRoles; } - static QV4::ReturnedValue get_hasModelChildren(QV4::CallContext *ctx) + static void get_hasModelChildren(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped o(scope, ctx->thisObject().as()); + QV4::Scoped o(scope, callData->thisObject.as()); if (!o) - return ctx->engine()->throwTypeError(QStringLiteral("Not a valid VisualData object")); + RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"))); const QQmlAdaptorModel *const model = static_cast(o->d()->item)->type->model; if (o->d()->item->index >= 0 && *model) { const QAbstractItemModel * const aim = model->aim(); - return QV4::Encode(aim->hasChildren(aim->index(o->d()->item->index, 0, model->rootIndex))); + RETURN_RESULT(QV4::Encode(aim->hasChildren(aim->index(o->d()->item->index, 0, model->rootIndex)))); } else { - return QV4::Encode(false); + RETURN_RESULT(QV4::Encode(false)); } } @@ -583,27 +581,25 @@ public: } } - static QV4::ReturnedValue get_modelData(QV4::CallContext *ctx) + static void get_modelData(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped o(scope, ctx->thisObject().as()); + QV4::Scoped o(scope, callData->thisObject.as()); if (!o) - return ctx->engine()->throwTypeError(QStringLiteral("Not a valid VisualData object")); + RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"))); - return scope.engine->fromVariant(static_cast(o->d()->item)->cachedData); + RETURN_RESULT(scope.engine->fromVariant(static_cast(o->d()->item)->cachedData)); } - static QV4::ReturnedValue set_modelData(QV4::CallContext *ctx) + static void set_modelData(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { - QV4::Scope scope(ctx); - QV4::Scoped o(scope, ctx->thisObject().as()); + QV4::Scoped o(scope, callData->thisObject.as()); if (!o) - return ctx->engine()->throwTypeError(QStringLiteral("Not a valid VisualData object")); - if (!ctx->argc()) - return ctx->engine()->throwTypeError(); + RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"))); + if (!callData->argc) + RETURN_RESULT(scope.engine->throwTypeError()); - static_cast(o->d()->item)->setModelData(scope.engine->toVariant(ctx->args()[0], QVariant::Invalid)); - return QV4::Encode::undefined(); + static_cast(o->d()->item)->setModelData(scope.engine->toVariant(callData->args[0], QVariant::Invalid)); + RETURN_RESULT(QV4::Encode::undefined()); } QV4::ReturnedValue get() -- cgit v1.2.3 From 8bc243f569e3feb1005fbca426bf24f59c38af2e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 19 May 2017 15:50:22 +0200 Subject: Move the engine() accessor from Object to Managed We can easily do this now that Managed has a pointer to an internal class (which always has a back pointer to the ExecutionEngine). Remove the extra engine pointer from ExecutionContext, and clean up tow methods in String. Change-Id: I98d750b1afbdeadf42e66ae0c92c48db1a7adc31 Reviewed-by: Robin Burchell --- .../qmldbg_debugger/qv4datacollector.cpp | 2 +- .../qmltooling/qmldbg_debugger/qv4debugger.cpp | 2 +- .../qqmlnativedebugservice.cpp | 6 +-- src/qml/jsapi/qjsvalue.cpp | 4 +- src/qml/jsruntime/qv4argumentsobject.cpp | 21 +++++----- src/qml/jsruntime/qv4context.cpp | 47 +++++++++------------- src/qml/jsruntime/qv4context_p.h | 17 +++----- src/qml/jsruntime/qv4engine.cpp | 4 +- src/qml/jsruntime/qv4functionobject.cpp | 12 +++--- src/qml/jsruntime/qv4globalobject.cpp | 2 +- src/qml/jsruntime/qv4managed_p.h | 1 + src/qml/jsruntime/qv4object.cpp | 18 ++++----- src/qml/jsruntime/qv4object_p.h | 2 - src/qml/jsruntime/qv4qmlcontext.cpp | 6 +-- src/qml/jsruntime/qv4qobjectwrapper.cpp | 4 +- src/qml/jsruntime/qv4scopedvalue_p.h | 2 +- src/qml/jsruntime/qv4string.cpp | 4 +- src/qml/jsruntime/qv4string_p.h | 6 +-- src/qml/jsruntime/qv4vme_moth.cpp | 2 +- src/qml/memory/qv4mm_p.h | 4 +- src/qml/qml/qqmlxmlhttprequest.cpp | 4 +- 21 files changed, 76 insertions(+), 94 deletions(-) (limited to 'src') diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp index b4b95f6713..755235a3a7 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp @@ -76,7 +76,7 @@ QV4::Heap::CallContext *QV4DataCollector::findScope(QV4::ExecutionContext *ctxt, if (!ctxt) return 0; - QV4::Scope s(ctxt->d()->engine); + QV4::Scope s(ctxt); QV4::ScopedContext ctx(s, ctxt); for (; scope > 0 && ctx; --scope) ctx = ctx->d()->outer; diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp index 9e8be84c33..b82df9c6a9 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp @@ -260,7 +260,7 @@ QV4::Function *QV4Debugger::getFunction() const if (QV4::Function *function = context->getFunction()) return function; else - return context->d()->engine->globalCode; + return m_engine->globalCode; } void QV4Debugger::runJobUnpaused() diff --git a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp index 7f842419e7..2015118d95 100644 --- a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp @@ -465,7 +465,7 @@ void NativeDebugger::handleVariables(QJsonObject *response, const QJsonObject &a } TRACE_PROTOCOL("Context: " << executionContext); - QV4::ExecutionEngine *engine = executionContext->d()->engine; + QV4::ExecutionEngine *engine = executionContext->engine(); if (!engine) { setError(response, QStringLiteral("No execution engine passed")); return; @@ -517,7 +517,7 @@ void NativeDebugger::handleExpressions(QJsonObject *response, const QJsonObject } TRACE_PROTOCOL("Context: " << executionContext); - QV4::ExecutionEngine *engine = executionContext->d()->engine; + QV4::ExecutionEngine *engine = executionContext->engine(); if (!engine) { setError(response, QStringLiteral("No execution engine passed")); return; @@ -669,7 +669,7 @@ QV4::Function *NativeDebugger::getFunction() const if (QV4::Function *function = context->getFunction()) return function; else - return context->d()->engine->globalCode; + return m_engine->globalCode; } void NativeDebugger::pauseAndWait() diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index bab2e633a7..3a3cf46ddb 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -1019,7 +1019,7 @@ QJSValue QJSValue::property(const QString& name) const if (idx < UINT_MAX) return property(idx); - s->makeIdentifier(engine); + s->makeIdentifier(); QV4::ScopedValue result(scope, o->get(s)); if (engine->hasException) result = engine->catchException(); @@ -1090,7 +1090,7 @@ void QJSValue::setProperty(const QString& name, const QJSValue& value) return; } - s->makeIdentifier(scope.engine); + s->makeIdentifier(); QV4::ScopedValue v(scope, QJSValuePrivate::convertedToValue(engine, value)); o->put(s, v); if (engine->hasException) diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index 9354bcb1a3..6ab838c387 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -53,13 +53,13 @@ void Heap::ArgumentsObject::init(QV4::CallContext *context) this->context = context->d(); Q_ASSERT(vtable() == QV4::ArgumentsObject::staticVTable()); - ExecutionEngine *v4 = context->d()->engine; + ExecutionEngine *v4 = context->engine(); Scope scope(v4); Scoped args(scope, this); if (context->d()->strictMode) { - Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(context->d()->engine->id_callee())); - Q_ASSERT(CallerPropertyIndex == args->internalClass()->find(context->d()->engine->id_caller())); + Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(v4->id_callee())); + Q_ASSERT(CallerPropertyIndex == args->internalClass()->find(v4->id_caller())); *args->propertyData(CalleePropertyIndex + QV4::Object::GetterOffset) = v4->thrower(); *args->propertyData(CalleePropertyIndex + QV4::Object::SetterOffset) = v4->thrower(); *args->propertyData(CallerPropertyIndex + QV4::Object::GetterOffset) = v4->thrower(); @@ -69,10 +69,10 @@ void Heap::ArgumentsObject::init(QV4::CallContext *context) args->arrayPut(0, context->args(), context->argc()); args->d()->fullyCreated = true; } else { - Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(context->d()->engine->id_callee())); + Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(v4->id_callee())); *args->propertyData(CalleePropertyIndex) = context->d()->function->asReturnedValue(); } - Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(context->d()->engine->id_length())); + Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(v4->id_length())); *args->propertyData(LengthPropertyIndex) = Primitive::fromInt32(context->d()->callData->argc); } @@ -81,18 +81,19 @@ void ArgumentsObject::fullyCreate() if (fullyCreated()) return; + Scope scope(engine()); + uint argCount = context()->callData->argc; uint numAccessors = qMin(context()->formalParameterCount(), argCount); ArrayData::realloc(this, Heap::ArrayData::Sparse, argCount, true); - context()->engine->requireArgumentsAccessors(numAccessors); + scope.engine->requireArgumentsAccessors(numAccessors); - Scope scope(engine()); Scoped md(scope, d()->mappedArguments); if (numAccessors) { - d()->mappedArguments = md->allocate(engine(), numAccessors); + d()->mappedArguments = md->allocate(scope.engine, numAccessors); for (uint i = 0; i < numAccessors; ++i) { d()->mappedArguments->data[i] = context()->callData->args[i]; - arraySet(i, context()->engine->argumentsAccessors + i, Attr_Accessor); + arraySet(i, scope.engine->argumentsAccessors + i, Attr_Accessor); } } arrayPut(numAccessors, context()->callData->args + numAccessors, argCount - numAccessors); @@ -114,7 +115,7 @@ bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, con uint numAccessors = qMin((int)context()->formalParameterCount(), context()->callData->argc); if (pd && index < (uint)numAccessors) isMapped = arrayData()->attributes(index).isAccessor() && - pd->getter() == context()->engine->argumentsAccessors[index].getter(); + pd->getter() == scope.engine->argumentsAccessors[index].getter(); if (isMapped) { Q_ASSERT(arrayData()); diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 03595aa59d..3ff864d7b9 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -66,9 +66,9 @@ DEFINE_MANAGED_VTABLE(GlobalContext); Heap::CallContext *ExecutionContext::newCallContext(Function *function, CallData *callData) { - Heap::CallContext *c = d()->engine->memoryManager->allocManaged( + Heap::CallContext *c = engine()->memoryManager->allocManaged( requiredMemoryForExecutionContect(function, callData->argc)); - c->init(d()->engine, Heap::ExecutionContext::Type_CallContext); + c->init(Heap::ExecutionContext::Type_CallContext); c->v4Function = function; @@ -95,27 +95,16 @@ Heap::CallContext *ExecutionContext::newCallContext(Function *function, CallData return c; } -Heap::CallContext *Heap::CallContext::createSimpleContext(ExecutionEngine *v4) -{ - Heap::CallContext *ctxt = v4->memoryManager->allocSimpleCallContext(v4); - return ctxt; -} - -void Heap::CallContext::freeSimpleCallContext() -{ - engine->memoryManager->freeSimpleCallContext(); -} - Heap::WithContext *ExecutionContext::newWithContext(Heap::Object *with) { - return d()->engine->memoryManager->alloc(d(), with); + return engine()->memoryManager->alloc(d(), with); } Heap::CatchContext *ExecutionContext::newCatchContext(Heap::String *exceptionVarName, ReturnedValue exceptionValue) { Scope scope(this); ScopedValue e(scope, exceptionValue); - return d()->engine->memoryManager->alloc(d(), exceptionVarName, e); + return engine()->memoryManager->alloc(d(), exceptionVarName, e); } void ExecutionContext::createMutableBinding(String *name, bool deletable) @@ -165,14 +154,14 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable) void Heap::GlobalContext::init(ExecutionEngine *eng) { - Heap::ExecutionContext::init(eng, Heap::ExecutionContext::Type_GlobalContext); + Heap::ExecutionContext::init(Heap::ExecutionContext::Type_GlobalContext); global = eng->globalObject->d(); } void Heap::CatchContext::init(ExecutionContext *outerContext, String *exceptionVarName, const Value &exceptionValue) { - Heap::ExecutionContext::init(outerContext->engine, Heap::ExecutionContext::Type_CatchContext); + Heap::ExecutionContext::init(Heap::ExecutionContext::Type_CatchContext); outer = outerContext; strictMode = outer->strictMode; callData = outer->callData; @@ -209,7 +198,7 @@ unsigned int CallContext::variableCount() const bool ExecutionContext::deleteProperty(String *name) { - name->makeIdentifier(engine()); + name->makeIdentifier(); Identifier *id = name->identifier(); Scope scope(this); @@ -339,7 +328,7 @@ void QV4::ExecutionContext::simpleCall(Scope &scope, CallData *callData, Functio ExecutionContextSaver ctxSaver(scope); - CallContext::Data *ctx = scope.engine->memoryManager->allocSimpleCallContext(scope.engine); + CallContext::Data *ctx = scope.engine->memoryManager->allocSimpleCallContext(); ctx->strictMode = function->isStrict(); ctx->callData = callData; @@ -364,7 +353,7 @@ void QV4::ExecutionContext::simpleCall(Scope &scope, CallData *callData, Functio void ExecutionContext::setProperty(String *name, const Value &value) { - name->makeIdentifier(engine()); + name->makeIdentifier(); Identifier *id = name->identifier(); Scope scope(this); @@ -428,21 +417,21 @@ void ExecutionContext::setProperty(String *name, const Value &value) } } - if (d()->strictMode || name->equals(d()->engine->id_this())) { + if (d()->strictMode || name->equals(engine()->id_this())) { ScopedValue n(scope, name->asReturnedValue()); engine()->throwReferenceError(n); return; } - d()->engine->globalObject->put(name, value); + engine()->globalObject->put(name, value); } ReturnedValue ExecutionContext::getProperty(String *name) { Scope scope(this); ScopedValue v(scope); - name->makeIdentifier(scope.engine); + name->makeIdentifier(); - if (name->equals(d()->engine->id_this())) + if (name->equals(engine()->id_this())) return thisObject().asReturnedValue(); bool hasWith = false; @@ -479,7 +468,7 @@ ReturnedValue ExecutionContext::getProperty(String *name) case Heap::ExecutionContext::Type_SimpleCallContext: { Heap::CallContext *c = static_cast(ctx->d()); if (c->v4Function && (c->v4Function->needsActivation() || hasWith || hasCatchScope)) { - name->makeIdentifier(engine()); + name->makeIdentifier(); Identifier *id = name->identifier(); uint index = c->v4Function->internalClass->find(id); @@ -520,9 +509,9 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) Scope scope(this); ScopedValue v(scope); base->setM(0); - name->makeIdentifier(scope.engine); + name->makeIdentifier(); - if (name->equals(d()->engine->id_this())) + if (name->equals(engine()->id_this())) return thisObject().asReturnedValue(); bool hasWith = false; @@ -560,7 +549,7 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) case Heap::ExecutionContext::Type_SimpleCallContext: { Heap::CallContext *c = static_cast(ctx->d()); if (c->v4Function && (c->v4Function->needsActivation() || hasWith || hasCatchScope)) { - name->makeIdentifier(engine()); + name->makeIdentifier(); Identifier *id = name->identifier(); uint index = c->v4Function->internalClass->find(id); @@ -600,7 +589,7 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) Function *ExecutionContext::getFunction() const { - Scope scope(d()->engine); + Scope scope(engine()); ScopedContext it(scope, this->d()); for (; it; it = it->d()->outer) { if (const CallContext *callCtx = it->asCallContext()) diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index 89ff6dc957..0b63922a4b 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -105,7 +105,6 @@ struct QmlContext; // can use the Members macro struct ExecutionContextData { CallData *callData; - ExecutionEngine *engine; ExecutionContext *outer; Lookup *lookups; const QV4::Value *constantTable; @@ -120,8 +119,7 @@ struct ExecutionContextData { Q_STATIC_ASSERT(std::is_standard_layout::value); Q_STATIC_ASSERT(offsetof(ExecutionContextData, callData) == 0); -Q_STATIC_ASSERT(offsetof(ExecutionContextData, engine) == offsetof(ExecutionContextData, callData) + QT_POINTER_SIZE); -Q_STATIC_ASSERT(offsetof(ExecutionContextData, outer) == offsetof(ExecutionContextData, engine) + QT_POINTER_SIZE); +Q_STATIC_ASSERT(offsetof(ExecutionContextData, outer) == offsetof(ExecutionContextData, callData) + QT_POINTER_SIZE); Q_STATIC_ASSERT(offsetof(ExecutionContextData, lookups) == offsetof(ExecutionContextData, outer) + QT_POINTER_SIZE); Q_STATIC_ASSERT(offsetof(ExecutionContextData, constantTable) == offsetof(ExecutionContextData, lookups) + QT_POINTER_SIZE); Q_STATIC_ASSERT(offsetof(ExecutionContextData, compilationUnit) == offsetof(ExecutionContextData, constantTable) + QT_POINTER_SIZE); @@ -141,11 +139,10 @@ struct ExecutionContext : Base, public ExecutionContextData { Type_CallContext = 0x6 }; - void init(ExecutionEngine *engine, ContextType t) + void init(ContextType t) { Base::init(); - this->engine = engine; type = t; lineNumber = -1; } @@ -172,12 +169,10 @@ struct CallContextSizeStruct : public ExecutionContext, public CallContextData { struct CallContext : ExecutionContext, public CallContextData { static Q_CONSTEXPR size_t baseOffset = sizeof(CallContextSizeStruct) - sizeof(CallContextData); - static CallContext *createSimpleContext(ExecutionEngine *v4); - void freeSimpleCallContext(); - void init(ExecutionEngine *engine, ContextType t = Type_SimpleCallContext) + void init(ContextType t = Type_SimpleCallContext) { - ExecutionContext::init(engine, t); + ExecutionContext::init(t); } inline unsigned int formalParameterCount() const; @@ -204,7 +199,7 @@ V4_ASSERT_IS_TRIVIAL(CatchContext) struct WithContext : ExecutionContext { void init(ExecutionContext *outerContext, Object *with) { - Heap::ExecutionContext::init(outerContext->engine, Heap::ExecutionContext::Type_WithContext); + Heap::ExecutionContext::init(Heap::ExecutionContext::Type_WithContext); outer = outerContext; callData = outer->callData; lookups = outer->lookups; @@ -230,8 +225,6 @@ struct Q_QML_EXPORT ExecutionContext : public Managed Q_MANAGED_TYPE(ExecutionContext) V4_INTERNALCLASS(ExecutionContext) - ExecutionEngine *engine() const { return d()->engine; } - Heap::CallContext *newCallContext(Function *f, CallData *callData); Heap::WithContext *newWithContext(Heap::Object *with); Heap::CatchContext *newCatchContext(Heap::String *exceptionVarName, ReturnedValue exceptionValue); diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index cb666451bb..0cb1b1ee13 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -868,8 +868,8 @@ static inline char *v4StackTrace(const ExecutionContext *context) QString result; QTextStream str(&result); str << "stack=["; - if (context && context->d()->engine) { - const QVector stackTrace = context->d()->engine->stackTrace(20); + if (context && context->engine()) { + const QVector stackTrace = context->engine()->stackTrace(20); for (int i = 0; i < stackTrace.size(); ++i) { if (i) str << ','; diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index bd6eb17bb4..9eb9d2ad36 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -122,7 +122,7 @@ void FunctionObject::init(String *n, bool createProto) Q_ASSERT(internalClass() && internalClass()->find(s.engine->id_prototype()) == Heap::FunctionObject::Index_Prototype); if (createProto) { - ScopedObject proto(s, scope()->engine->newObject(s.engine->internalClasses[EngineBase::Class_ObjectProto], s.engine->objectPrototype())); + ScopedObject proto(s, s.engine->newObject(s.engine->internalClasses[EngineBase::Class_ObjectProto], s.engine->objectPrototype())); Q_ASSERT(s.engine->internalClasses[EngineBase::Class_ObjectProto]->find(s.engine->id_constructor()) == Heap::FunctionObject::Index_ProtoConstructor); *proto->propertyData(Heap::FunctionObject::Index_ProtoConstructor) = this->asReturnedValue(); *propertyData(Heap::FunctionObject::Index_Prototype) = proto.asReturnedValue(); @@ -136,7 +136,7 @@ void FunctionObject::init(String *n, bool createProto) ReturnedValue FunctionObject::name() const { - return get(scope()->engine->id_name()); + return get(scope()->internalClass->engine->id_name()); } void FunctionObject::construct(const Managed *that, Scope &scope, CallData *) @@ -160,7 +160,7 @@ void FunctionObject::markObjects(Heap::Base *that, ExecutionEngine *e) Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *scope, Function *function) { - return scope->d()->engine->memoryManager->allocObject(scope, function); + return scope->engine()->memoryManager->allocObject(scope, function); } bool FunctionObject::isBinding() const @@ -439,8 +439,8 @@ void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function ScopedProperty pd(s); pd->value = s.engine->thrower(); pd->set = s.engine->thrower(); - f->insertMember(scope->d()->engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); - f->insertMember(scope->d()->engine->id_arguments(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + f->insertMember(s.engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + f->insertMember(s.engine->id_arguments(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); } } @@ -497,7 +497,7 @@ void IndexedBuiltinFunction::call(const Managed *that, Scope &scope, CallData *c ExecutionContextSaver ctxSaver(scope); - CallContext::Data *ctx = v4->memoryManager->allocSimpleCallContext(v4); + CallContext::Data *ctx = v4->memoryManager->allocSimpleCallContext(); ctx->strictMode = f->scope()->strictMode; // ### needed? scope or parent context? ctx->callData = callData; v4->pushContext(ctx); diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp index f0630660d4..0916e8e110 100644 --- a/src/qml/jsruntime/qv4globalobject.cpp +++ b/src/qml/jsruntime/qv4globalobject.cpp @@ -332,8 +332,8 @@ DEFINE_OBJECT_VTABLE(EvalFunction); void Heap::EvalFunction::init(QV4::ExecutionContext *scope) { - Heap::FunctionObject::init(scope, scope->d()->engine->id_eval()); Scope s(scope); + Heap::FunctionObject::init(scope, s.engine->id_eval()); ScopedFunctionObject f(s, this); f->defineReadonlyProperty(s.engine->id_length(), Primitive::fromInt32(1)); } diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h index 4c387a7fe7..6859334797 100644 --- a/src/qml/jsruntime/qv4managed_p.h +++ b/src/qml/jsruntime/qv4managed_p.h @@ -199,6 +199,7 @@ public: Q_MANAGED_TYPE(Invalid) InternalClass *internalClass() const { return d()->internalClass; } + inline ExecutionEngine *engine() const { return internalClass()->engine; } bool isListType() const { return d()->vtable()->type == Type_QmlSequence; } diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 04336d4f88..98f5c7464f 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -254,7 +254,7 @@ void Object::getOwnProperty(String *name, PropertyAttributes *attrs, Property *p if (idx != UINT_MAX) return getOwnProperty(idx, attrs, p); - name->makeIdentifier(engine()); + name->makeIdentifier(); Identifier *id = name->identifier(); uint member = internalClass()->find(id); @@ -299,7 +299,7 @@ Value *Object::getValueOrSetter(String *name, PropertyAttributes *attrs) { Q_ASSERT(name->asArrayIndex() == UINT_MAX); - name->makeIdentifier(engine()); + name->makeIdentifier(); Identifier *id = name->identifier(); Heap::Object *o = d(); @@ -377,7 +377,7 @@ bool Object::hasOwnProperty(String *name) const if (idx != UINT_MAX) return hasOwnProperty(idx); - name->makeIdentifier(engine()); + name->makeIdentifier(); Identifier *id = name->identifier(); if (internalClass()->find(id) < UINT_MAX) @@ -437,7 +437,7 @@ PropertyAttributes Object::query(const Managed *m, String *name) if (idx != UINT_MAX) return queryIndexed(m, idx); - name->makeIdentifier(m->internalClass()->engine); + name->makeIdentifier(); Identifier *id = name->identifier(); const Object *o = static_cast(m); @@ -640,10 +640,10 @@ ReturnedValue Object::internalGet(String *name, bool *hasProperty) const if (idx != UINT_MAX) return getIndexed(idx, hasProperty); - Scope scope(engine()); - name->makeIdentifier(scope.engine); + name->makeIdentifier(); Identifier *id = name->identifier(); + Scope scope(engine()); ScopedObject o(scope, this); while (o) { uint idx = o->internalClass()->find(id); @@ -708,7 +708,7 @@ void Object::internalPut(String *name, const Value &value) if (idx != UINT_MAX) return putIndexed(idx, value); - name->makeIdentifier(engine()); + name->makeIdentifier(); Identifier *id = name->identifier(); uint member = internalClass()->find(id); @@ -862,7 +862,7 @@ bool Object::internalDeleteProperty(String *name) if (idx != UINT_MAX) return deleteIndexedProperty(idx); - name->makeIdentifier(engine()); + name->makeIdentifier(); uint memberIdx = internalClass()->find(name->identifier()); if (memberIdx != UINT_MAX) { @@ -901,7 +901,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const return __defineOwnProperty__(engine, idx, p, attrs); Scope scope(engine); - name->makeIdentifier(scope.engine); + name->makeIdentifier(); uint memberIndex; diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index a9afe14129..9592ff5d1b 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -273,8 +273,6 @@ struct Q_QML_EXPORT Object: Managed { } void insertMember(String *s, const Property *p, PropertyAttributes attributes); - inline ExecutionEngine *engine() const { return internalClass()->engine; } - bool isExtensible() const { return d()->internalClass->extensible; } // Array handling diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index ef1a1c11ed..91d65a70c9 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -300,7 +300,7 @@ void QmlContextWrapper::put(Managed *m, String *name, const Value &value) void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QmlContextWrapper *qml) { - Heap::ExecutionContext::init(outerContext->engine(), Heap::ExecutionContext::Type_QmlContext); + Heap::ExecutionContext::init(Heap::ExecutionContext::Type_QmlContext); outer = outerContext->d(); strictMode = false; callData = outer->callData; @@ -330,7 +330,7 @@ Heap::QmlContext *QmlContext::createWorkerContext(ExecutionContext *parent, cons qml->QV4::Object::put(QV4::ScopedString(scope, scope.engine->newString(QStringLiteral("WorkerScript"))), api); qml->setReadOnly(true); - Heap::QmlContext *c = parent->d()->engine->memoryManager->alloc(parent, qml); + Heap::QmlContext *c = scope.engine->memoryManager->alloc(parent, qml); Q_ASSERT(c->vtable() == staticVTable()); return c; } @@ -340,7 +340,7 @@ Heap::QmlContext *QmlContext::create(ExecutionContext *parent, QQmlContextData * Scope scope(parent); Scoped qml(scope, scope.engine->memoryManager->allocObject(context, scopeObject)); - Heap::QmlContext *c = parent->d()->engine->memoryManager->alloc(parent, qml); + Heap::QmlContext *c = scope.engine->memoryManager->alloc(parent, qml); Q_ASSERT(c->vtable() == staticVTable()); return c; } diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index f484d56040..d7978cc212 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -1688,7 +1688,7 @@ QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine) ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, int index) { Scope valueScope(scope); - Scoped method(valueScope, scope->d()->engine->memoryManager->allocObject(scope)); + Scoped method(valueScope, valueScope.engine->memoryManager->allocObject(scope)); method->d()->setObject(object); if (QQmlData *ddata = QQmlData::get(object)) @@ -1739,7 +1739,7 @@ QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionContext *ctx) co result = QLatin1String("null"); } - return ctx->d()->engine->newString(result)->asReturnedValue(); + return ctx->engine()->newString(result)->asReturnedValue(); } QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionContext *ctx, const Value *args, int argc) const diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h index bc882bbd95..04a0c74133 100644 --- a/src/qml/jsruntime/qv4scopedvalue_p.h +++ b/src/qml/jsruntime/qv4scopedvalue_p.h @@ -102,7 +102,7 @@ struct ScopedValue; struct Scope { inline Scope(ExecutionContext *ctx) - : engine(ctx->d()->engine) + : engine(ctx->engine()) , mark(engine->jsStackTop) , result(*engine->jsAlloca(1)) { diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp index cde2131aab..1404ab6d9b 100644 --- a/src/qml/jsruntime/qv4string.cpp +++ b/src/qml/jsruntime/qv4string.cpp @@ -141,12 +141,12 @@ uint String::toUInt(bool *ok) const return UINT_MAX; } -void String::makeIdentifierImpl(ExecutionEngine *e) const +void String::makeIdentifierImpl() const { if (d()->largestSubLength) d()->simplifyString(); Q_ASSERT(!d()->largestSubLength); - e->identifierTable->identifier(this); + engine()->identifierTable->identifier(this); } void Heap::String::simplifyString() const diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index ad30165ce5..458a9edae6 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -176,13 +176,13 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed { } uint toUInt(bool *ok) const; - void makeIdentifier(ExecutionEngine *e) const { + void makeIdentifier() const { if (d()->identifier) return; - makeIdentifierImpl(e); + makeIdentifierImpl(); } - void makeIdentifierImpl(ExecutionEngine *e) const; + void makeIdentifierImpl() const; static uint createHashValue(const QChar *ch, int length, uint *subtype) { diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index d662b1738d..9d65f67f0f 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -172,7 +172,7 @@ static QV4::Function *qt_v4ExtractFunction(QV4::ExecutionContext *context) if (QV4::Function *function = context->getFunction()) return function; else - return context->d()->engine->globalCode; + return context->engine()->globalCode; } static void qt_v4TriggerBreakpoint(const Breakpoint &bp, QV4::Function *function) diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h index 69d3eeb93c..c5334a0dde 100644 --- a/src/qml/memory/qv4mm_p.h +++ b/src/qml/memory/qv4mm_p.h @@ -206,13 +206,13 @@ public: Q_DECL_CONSTEXPR static inline std::size_t align(std::size_t size) { return (size + Chunk::SlotSize - 1) & ~(Chunk::SlotSize - 1); } - QV4::Heap::CallContext *allocSimpleCallContext(QV4::ExecutionEngine *v4) + QV4::Heap::CallContext *allocSimpleCallContext() { Heap::CallContext *ctxt = stackAllocator.allocate(); memset(ctxt, 0, sizeof(Heap::CallContext)); ctxt->internalClass = CallContext::defaultInternalClass(engine); Q_ASSERT(ctxt->internalClass && ctxt->internalClass->vtable); - ctxt->init(v4); + ctxt->init(); return ctxt; } diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index d0d9f080da..f3a39313c1 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -895,7 +895,7 @@ ReturnedValue NamedNodeMap::get(const Managed *m, String *name, bool *hasPropert const NamedNodeMap *r = static_cast(m); QV4::ExecutionEngine *v4 = r->engine(); - name->makeIdentifier(v4); + name->makeIdentifier(); if (name->equals(v4->id_length())) return Primitive::fromInt32(r->d()->list().count()).asReturnedValue(); @@ -940,7 +940,7 @@ ReturnedValue NodeList::get(const Managed *m, String *name, bool *hasProperty) const NodeList *r = static_cast(m); QV4::ExecutionEngine *v4 = r->engine(); - name->makeIdentifier(v4); + name->makeIdentifier(); if (name->equals(v4->id_length())) return Primitive::fromInt32(r->d()->d->children.count()).asReturnedValue(); -- cgit v1.2.3 From a5dad4e78ff78777eafbae723442db4e9da2d6ff Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 19 May 2017 15:57:13 +0200 Subject: Get rid of the MemoryManager pointer inside String We can always get the pointer through the internalClass. Change-Id: If68432845e7c67da70d9e19aef1a90ebe1e6056b Reviewed-by: Robin Burchell --- src/qml/jsruntime/qv4function.cpp | 4 ++-- src/qml/jsruntime/qv4runtime.cpp | 4 ++-- src/qml/jsruntime/qv4string.cpp | 10 ++++------ src/qml/jsruntime/qv4string_p.h | 5 ++--- src/qml/memory/qv4mm_p.h | 2 +- 5 files changed, 11 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index 994dede26d..31b57b97e9 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -74,7 +74,7 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, } // duplicate arguments, need some trick to store them MemoryManager *mm = engine->memoryManager; - arg = mm->alloc(mm, arg->d(), engine->newString(QString(0xfffe))); + arg = mm->alloc(arg->d(), engine->newString(QString(0xfffe))); } } nFormals = compiledFunction->nFormals; @@ -109,7 +109,7 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QListmemoryManager->alloc(engine->memoryManager, arg->d(), engine->newString(QString(0xfffe))); + arg = engine->memoryManager->alloc(arg->d(), engine->newString(QString(0xfffe))); } } nFormals = parameters.size(); diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index a79eab3778..37a2bfdf90 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -554,7 +554,7 @@ QV4::ReturnedValue RuntimeHelpers::addHelper(ExecutionEngine *engine, const Valu if (!sright->d()->length()) return sleft->asReturnedValue(); MemoryManager *mm = engine->memoryManager; - return (mm->alloc(mm, sleft->d(), sright->d()))->asReturnedValue(); + return (mm->alloc(sleft->d(), sright->d()))->asReturnedValue(); } double x = RuntimeHelpers::toNumber(pleft); double y = RuntimeHelpers::toNumber(pright); @@ -586,7 +586,7 @@ QV4::ReturnedValue Runtime::method_addString(ExecutionEngine *engine, const Valu if (!sright->d()->length()) return pleft->asReturnedValue(); MemoryManager *mm = engine->memoryManager; - return (mm->alloc(mm, sleft->d(), sright->d()))->asReturnedValue(); + return (mm->alloc(sleft->d(), sright->d()))->asReturnedValue(); } void Runtime::method_setProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value) diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp index 1404ab6d9b..515d61c8e4 100644 --- a/src/qml/jsruntime/qv4string.cpp +++ b/src/qml/jsruntime/qv4string.cpp @@ -75,10 +75,9 @@ bool String::isEqualTo(Managed *t, Managed *o) } -void Heap::String::init(MemoryManager *mm, const QString &t) +void Heap::String::init(const QString &t) { Base::init(); - this->mm = mm; subtype = String::StringType_Unknown; @@ -90,10 +89,9 @@ void Heap::String::init(MemoryManager *mm, const QString &t) len = text->size; } -void Heap::String::init(MemoryManager *mm, String *l, String *r) +void Heap::String::init(String *l, String *r) { Base::init(); - this->mm = mm; subtype = String::StringType_Unknown; @@ -116,7 +114,7 @@ void Heap::String::init(MemoryManager *mm, String *l, String *r) void Heap::String::destroy() { if (!largestSubLength) { - mm->changeUnmanagedHeapSizeUsage(qptrdiff(-text->size) * (int)sizeof(QChar)); + internalClass->engine->memoryManager->changeUnmanagedHeapSizeUsage(qptrdiff(-text->size) * (int)sizeof(QChar)); if (!text->ref.deref()) QStringData::deallocate(text); } @@ -161,7 +159,7 @@ void Heap::String::simplifyString() const text->ref.ref(); identifier = 0; largestSubLength = 0; - mm->changeUnmanagedHeapSizeUsage(qptrdiff(text->size) * (qptrdiff)sizeof(QChar)); + internalClass->engine->memoryManager->changeUnmanagedHeapSizeUsage(qptrdiff(text->size) * (qptrdiff)sizeof(QChar)); } void Heap::String::append(const String *data, QChar *ch) diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index 458a9edae6..f5311ae5d4 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -72,8 +72,8 @@ struct Q_QML_PRIVATE_EXPORT String : Base { }; #ifndef V4_BOOTSTRAP - void init(MemoryManager *mm, const QString &text); - void init(MemoryManager *mm, String *l, String *n); + void init(const QString &text); + void init(String *l, String *n); void destroy(); void simplifyString() const; int length() const { @@ -126,7 +126,6 @@ struct Q_QML_PRIVATE_EXPORT String : Base { mutable uint stringHash; mutable uint largestSubLength; uint len; - MemoryManager *mm; private: static void append(const String *data, QChar *ch); #endif diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h index c5334a0dde..77c5885dfe 100644 --- a/src/qml/memory/qv4mm_p.h +++ b/src/qml/memory/qv4mm_p.h @@ -261,7 +261,7 @@ public: typename ManagedType::Data *o = reinterpret_cast(allocString(unmanagedSize)); o->internalClass = ManagedType::defaultInternalClass(engine); Q_ASSERT(o->internalClass && o->internalClass->vtable); - o->init(this, arg1); + o->init(arg1); return o; } -- cgit v1.2.3 From 96f6ba5f562073f508cd8569ac354592fdd48f4a Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 11 May 2017 20:36:51 +0200 Subject: Revert "Temporarily restore QQuickPointerDevice::pointerEvent() accessor" This reverts commit ee6b07b3ce8ba80632868181d45d96253acb1064. This is to be integrated after the qtlocation change to remove the dependency on this private function. Task-number: QTBUG-57253 Change-Id: I756681fb2595d1326b7e5206bac57ccc318c0a46 Reviewed-by: Liang Qi --- src/quick/items/qquickevents_p_p.h | 7 ------- src/quick/items/qquickwindow.cpp | 5 +---- 2 files changed, 1 insertion(+), 11 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 323ecfa4ff..3735d68a85 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -501,7 +501,6 @@ public: : m_deviceType(devType), m_pointerType(pType), m_capabilities(caps) , m_maximumTouchPoints(maxPoints), m_buttonCount(buttonCount), m_name(name) , m_uniqueId(QPointingDeviceUniqueId::fromNumericId(uniqueId)) - , m_event(nullptr) { } @@ -514,8 +513,6 @@ public: QString name() const { return m_name; } QPointingDeviceUniqueId uniqueId() const { return m_uniqueId; } - QQuickPointerEvent *pointerEvent() const { return m_event; } // deprecated - static QQuickPointerDevice *touchDevice(QTouchDevice *d); static QList touchDevices(); static QQuickPointerDevice *genericMouseDevice(); @@ -530,10 +527,6 @@ private: QString m_name; QPointingDeviceUniqueId m_uniqueId; - // the event instance used last time within the context of one window - QQuickPointerEvent *m_event; // deprecated - friend class QQuickWindowPrivate; // not needed after removing the above - Q_DISABLE_COPY(QQuickPointerDevice) }; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 816c057ab0..c441cfc357 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2117,10 +2117,8 @@ QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QQuickPointerDevic { // the list of devices should be very small so a linear search should be ok for (QQuickPointerEvent *e: pointerEventInstances) { - if (e->device() == device) { - device->m_event = e; + if (e->device() == device) return e; - } } QQuickPointerEvent *ev = nullptr; @@ -2138,7 +2136,6 @@ QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QQuickPointerDevic break; } pointerEventInstances << ev; - device->m_event = ev; return ev; } -- cgit v1.2.3 From 3f9367cb32533b691cb8c761213f21a524e3d1cb Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 22 May 2017 08:34:00 +0200 Subject: Fix compilation of QtScxml QtScxml was using internal API in v4, that has changed. Restore the old function signature until all it's uses have been cleaned up. Task-number: QTBUG-60938 Change-Id: Ie40c09da9df9e5684972669cd9a511a868b920a4 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4string_p.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index f5311ae5d4..d9625e3f4e 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -175,6 +175,10 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed { } uint toUInt(bool *ok) const; + Q_DECL_DEPRECATED void makeIdentifier(ExecutionEngine *) { + makeIdentifier(); + } + void makeIdentifier() const { if (d()->identifier) return; -- cgit v1.2.3 From 16105b1b0cf9dc3849d9ff03503fa8bed1b8da40 Mon Sep 17 00:00:00 2001 From: Marco Benelli Date: Fri, 19 May 2017 11:27:48 +0200 Subject: Update qmltypes Small update of qmltypes. Change-Id: I5408f0ae50a70ca1c1cc0c0deaa8ddf6458c88c1 Reviewed-by: Thomas Hartmann Reviewed-by: Erik Verbruggen --- src/imports/builtins/builtins.qmltypes | 3 ++- src/imports/qtquick2/plugins.qmltypes | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/imports/builtins/builtins.qmltypes b/src/imports/builtins/builtins.qmltypes index ac95a8837b..c2f8f5b521 100644 --- a/src/imports/builtins/builtins.qmltypes +++ b/src/imports/builtins/builtins.qmltypes @@ -426,7 +426,8 @@ Module { "WA_X11DoNotAcceptFocus": 126, "WA_MacNoShadow": 127, "WA_AlwaysStackOnTop": 128, - "WA_AttributeCount": 129 + "WA_TabletTracking": 129, + "WA_AttributeCount": 130 } } Enum { diff --git a/src/imports/qtquick2/plugins.qmltypes b/src/imports/qtquick2/plugins.qmltypes index a9534b5ccc..834b4bfac2 100644 --- a/src/imports/qtquick2/plugins.qmltypes +++ b/src/imports/qtquick2/plugins.qmltypes @@ -1414,7 +1414,7 @@ Module { Property { name: "source"; type: "QObject"; isPointer: true } Property { name: "target"; type: "QObject"; isReadonly: true; isPointer: true } Property { name: "hotSpot"; type: "QPointF" } - Property { name: "imageSource"; revision: 8; type: "QUrl" } + Property { name: "imageSource"; type: "QUrl" } Property { name: "keys"; type: "QStringList" } Property { name: "mimeData"; type: "QVariantMap" } Property { name: "supportedActions"; type: "Qt::DropActions" } -- cgit v1.2.3 From 101bd41490be160be218aa638f35cfa609267a83 Mon Sep 17 00:00:00 2001 From: Andy Nichols Date: Tue, 23 May 2017 12:13:35 +0200 Subject: Software: Fix QQuickWidget rendering in QMdiArea It is not safe to assume that the paintRegion is always the same as the updateRegion reported by the software renderer, since in some cases more needs to be flushed, such as in the case of the QMdiArea. Now we make sure to unite both of these regions and flush everything needed. Task-number: QTBUG-60985 Change-Id: I0408c21e42dd4107b0974877144e8e93f2c30fae Reviewed-by: Laszlo Agocs --- src/quickwidgets/qquickwidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index 4d021cb680..49ac0caefd 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -1596,10 +1596,10 @@ QQuickWindow *QQuickWidget::quickWindow() const void QQuickWidget::paintEvent(QPaintEvent *event) { - Q_UNUSED(event) Q_D(QQuickWidget); if (d->useSoftwareRenderer) { QPainter painter(this); + d->updateRegion = d->updateRegion.united(event->region()); if (d->updateRegion.isNull()) { //Paint everything painter.drawImage(rect(), d->softwareImage); -- cgit v1.2.3 From 082d7361ae54cc9081a7e33ae7bd49949dfb3e08 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Tue, 23 May 2017 12:59:14 +0200 Subject: QtQml: Restrict alloca definition to MSVC Fix developer build with MinGW: In file included from memory\qv4mm.cpp:60:0: jsruntime/qv4alloca_p.h:62:0: error: "alloca" redefined [-Werror] # define alloca _alloca ^ In file included from .../mingw32/i686-w64-mingw32/include/stdlib.h:686:0, from .../mingw32/i686-w64-mingw32/include/c++/cstdlib:72, from .../mingw32/i686-w64-mingw32/include/c++/bits/stl_algo.h:59, from .../mingw32/i686-w64-mingw32/include/c++/algorithm:62, from .../qtbase/src/corelib/global/qglobal.h:109, from ...\qtbase\include/QtCore/qglobal.h:1, from jsruntime/qv4global_p.h:54, from jsruntime/qv4engine_p.h:53, from memory\qv4mm.cpp:40: .../mingw32/i686-w64-mingw32/include/malloc.h:183:0: note: this is the location of the previous definition #define alloca(x) __builtin_alloca((x)) Amends change a225bddf67f4786c845193630d4ab20b99a2fc3a. Change-Id: I4a758776dbf78225f62883393eb50b7121297a1b Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4alloca_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/qml/jsruntime/qv4alloca_p.h b/src/qml/jsruntime/qv4alloca_p.h index c21878fa42..1e9f83a90e 100644 --- a/src/qml/jsruntime/qv4alloca_p.h +++ b/src/qml/jsruntime/qv4alloca_p.h @@ -58,7 +58,7 @@ #elif QT_CONFIG(alloca_malloc_h) # include // This does not matter unless compiling in strict standard mode. -# ifdef Q_OS_WIN +# ifdef Q_CC_MSVC # define alloca _alloca # endif #else -- cgit v1.2.3 From 4b71719d1c02e484192e5b840e0937e1778d33c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCri=20Valdmann?= Date: Mon, 15 May 2017 12:24:41 +0200 Subject: Improve dirty region calculation in software renderer The software renderers dirty region calculation is done in integral logical coordinates, which leads to quite some trouble when handling scene graph elements with fractional coordinates (used by WebEngine for example). The optimal solution would probably be to either use integral physical coordinates or floating point logical coordinates, however this would seem to require substantial changes to QPainter and QBackingStore and so on. So, this patch instead changes the calculation to use something like interval arithmetic: instead of just rounding each logical coordinate to the nearest integer the renderer now uses (very carefully) both the upper and lower boundaries to make sure that the dirty regions always err on the side of caution. I expect this change to make rendering slower but only in situations where previously there would be rendering errors. Task-number: QTBUG-60393 Change-Id: I7f8e7d2739c810328c841130df9c1c7332439447 Reviewed-by: Andy Nichols --- .../software/qsgabstractsoftwarerenderer.cpp | 8 +-- .../software/qsgsoftwarerenderablenode.cpp | 77 +++++++++++++--------- .../software/qsgsoftwarerenderablenode_p.h | 6 +- 3 files changed, 55 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp index 2ff180ea99..02cf8209d1 100644 --- a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp @@ -165,12 +165,12 @@ QRegion QSGAbstractSoftwareRenderer::optimizeRenderList() // Keep up with obscured regions if (node->isOpaque()) { - m_obscuredRegion += QRegion(node->boundingRect()); + m_obscuredRegion += node->boundingRectMin(); } if (node->isDirty()) { // Don't paint things outside of the rendering area - if (!m_background->rect().toRect().contains(node->boundingRect(), /*proper*/ true)) { + if (!m_background->rect().toRect().contains(node->boundingRectMax(), /*proper*/ true)) { // Some part(s) of node is(are) outside of the rendering area QRegion renderArea(m_background->rect().toRect()); QRegion outsideRegions = node->dirtyRegion().subtracted(renderArea); @@ -181,7 +181,7 @@ QRegion QSGAbstractSoftwareRenderer::optimizeRenderList() // Get the dirty region's to pass to the next nodes if (node->isOpaque()) { // if isOpaque, subtract node's dirty rect from m_dirtyRegion - m_dirtyRegion -= node->dirtyRegion(); + m_dirtyRegion -= node->boundingRectMin(); } else { // if isAlpha, add node's dirty rect to m_dirtyRegion m_dirtyRegion += node->dirtyRegion(); @@ -264,7 +264,7 @@ void QSGAbstractSoftwareRenderer::nodeRemoved(QSGNode *node) // Need to mark this region dirty in the other nodes QRegion dirtyRegion = renderable->previousDirtyRegion(true); if (dirtyRegion.isEmpty()) - dirtyRegion = renderable->boundingRect(); + dirtyRegion = renderable->boundingRectMax(); m_dirtyRegion += dirtyRegion; m_nodes.remove(node); delete renderable; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp index 59c47db0c4..ce08d18057 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp @@ -54,10 +54,28 @@ #include #include +#include + Q_LOGGING_CATEGORY(lcRenderable, "qt.scenegraph.softwarecontext.renderable") QT_BEGIN_NAMESPACE +// Largest subrectangle with integer coordinates +inline QRect toRectMin(const QRectF & r) +{ + int x1 = qCeil(r.left()); + int x2 = qFloor(r.right()); + int y1 = qCeil(r.top()); + int y2 = qFloor(r.bottom()); + return QRect(x1, y1, x2 - x1, y2 - y1); +} + +// Smallest superrectangle with integer coordinates +inline QRect toRectMax(const QRectF & r) +{ + return r.toAlignedRect(); +} + QSGSoftwareRenderableNode::QSGSoftwareRenderableNode(NodeType type, QSGNode *node) : m_nodeType(type) , m_isOpaque(true) @@ -117,7 +135,7 @@ void QSGSoftwareRenderableNode::update() // Update the Node properties m_isDirty = true; - QRect boundingRect; + QRectF boundingRect; switch (m_nodeType) { case QSGSoftwareRenderableNode::SimpleRect: @@ -126,7 +144,7 @@ void QSGSoftwareRenderableNode::update() else m_isOpaque = false; - boundingRect = m_handle.simpleRectNode->rect().toRect(); + boundingRect = m_handle.simpleRectNode->rect(); break; case QSGSoftwareRenderableNode::SimpleTexture: if (!m_handle.simpleTextureNode->texture()->hasAlphaChannel() && !m_transform.isRotating()) @@ -134,7 +152,7 @@ void QSGSoftwareRenderableNode::update() else m_isOpaque = false; - boundingRect = m_handle.simpleTextureNode->rect().toRect(); + boundingRect = m_handle.simpleTextureNode->rect(); break; case QSGSoftwareRenderableNode::Image: // There isn't a way to tell, so assume it's not @@ -148,7 +166,7 @@ void QSGSoftwareRenderableNode::update() else m_isOpaque = false; - boundingRect = QRect(0, 0, m_handle.painterNode->size().width(), m_handle.painterNode->size().height()); + boundingRect = QRectF(0, 0, m_handle.painterNode->size().width(), m_handle.painterNode->size().height()); break; case QSGSoftwareRenderableNode::Rectangle: if (m_handle.rectangleNode->isOpaque() && !m_transform.isRotating()) @@ -156,19 +174,19 @@ void QSGSoftwareRenderableNode::update() else m_isOpaque = false; - boundingRect = m_handle.rectangleNode->rect().toRect(); + boundingRect = m_handle.rectangleNode->rect(); break; case QSGSoftwareRenderableNode::Glyph: // Always has alpha m_isOpaque = false; - boundingRect = m_handle.glpyhNode->boundingRect().toAlignedRect(); + boundingRect = m_handle.glpyhNode->boundingRect(); break; case QSGSoftwareRenderableNode::NinePatch: // Difficult to tell, assume non-opaque m_isOpaque = false; - boundingRect = m_handle.ninePatchNode->bounds().toRect(); + boundingRect = m_handle.ninePatchNode->bounds(); break; case QSGSoftwareRenderableNode::SimpleRectangle: if (m_handle.simpleRectangleNode->color().alpha() == 255 && !m_transform.isRotating()) @@ -176,7 +194,7 @@ void QSGSoftwareRenderableNode::update() else m_isOpaque = false; - boundingRect = m_handle.simpleRectangleNode->rect().toRect(); + boundingRect = m_handle.simpleRectangleNode->rect(); break; case QSGSoftwareRenderableNode::SimpleImage: if (!m_handle.simpleImageNode->texture()->hasAlphaChannel() && !m_transform.isRotating()) @@ -184,12 +202,12 @@ void QSGSoftwareRenderableNode::update() else m_isOpaque = false; - boundingRect = m_handle.simpleImageNode->rect().toRect(); + boundingRect = m_handle.simpleImageNode->rect(); break; #if QT_CONFIG(quick_sprite) case QSGSoftwareRenderableNode::SpriteNode: m_isOpaque = m_handle.spriteNode->isOpaque(); - boundingRect = m_handle.spriteNode->rect().toRect(); + boundingRect = m_handle.spriteNode->rect(); break; #endif case QSGSoftwareRenderableNode::RenderNode: @@ -198,27 +216,32 @@ void QSGSoftwareRenderableNode::update() else m_isOpaque = false; - boundingRect = m_handle.renderNode->rect().toRect(); + boundingRect = m_handle.renderNode->rect(); break; default: break; } - m_boundingRect = m_transform.mapRect(boundingRect); + const QRectF transformedRect = m_transform.mapRect(boundingRect); + m_boundingRectMin = toRectMin(transformedRect); + m_boundingRectMax = toRectMax(transformedRect); if (m_hasClipRegion && m_clipRegion.rectCount() <= 1) { // If there is a clipRegion, and it is empty, the item wont be rendered - if (m_clipRegion.isEmpty()) - m_boundingRect = QRect(); - else - m_boundingRect = m_boundingRect.intersected(m_clipRegion.rects().first()); + if (m_clipRegion.isEmpty()) { + m_boundingRectMin = QRect(); + m_boundingRectMax = QRect(); + } else { + m_boundingRectMin = m_boundingRectMin.intersected(m_clipRegion.rects().first()); + m_boundingRectMax = m_boundingRectMax.intersected(m_clipRegion.rects().first()); + } } // Overrides if (m_opacity < 1.0f) m_isOpaque = false; - m_dirtyRegion = QRegion(m_boundingRect); + m_dirtyRegion = QRegion(m_boundingRectMax); } struct RenderNodeState : public QSGRenderNode::RenderState @@ -258,7 +281,7 @@ QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaqu rs.cr = m_clipRegion; const QRect br = m_handle.renderNode->flags().testFlag(QSGRenderNode::BoundedRectRendering) - ? m_boundingRect : + ? m_boundingRectMax : QRect(0, 0, painter->device()->width(), painter->device()->height()); painter->save(); @@ -335,19 +358,13 @@ QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaqu painter->restore(); QRegion areaToBeFlushed = m_dirtyRegion; - m_previousDirtyRegion = QRegion(m_boundingRect); + m_previousDirtyRegion = QRegion(m_boundingRectMax); m_isDirty = false; m_dirtyRegion = QRegion(); return areaToBeFlushed; } -QRect QSGSoftwareRenderableNode::boundingRect() const -{ - // This returns the bounding area of a renderable node in world coordinates - return m_boundingRect; -} - bool QSGSoftwareRenderableNode::isDirtyRegionEmpty() const { return m_dirtyRegion.isEmpty(); @@ -392,12 +409,12 @@ void QSGSoftwareRenderableNode::markMaterialDirty() void QSGSoftwareRenderableNode::addDirtyRegion(const QRegion &dirtyRegion, bool forceDirty) { - // Check if the dirty region applys to this node + // Check if the dirty region applies to this node QRegion prev = m_dirtyRegion; - if (dirtyRegion.intersects(boundingRect())) { + if (dirtyRegion.intersects(m_boundingRectMax)) { if (forceDirty) m_isDirty = true; - m_dirtyRegion += dirtyRegion.intersected(boundingRect()); + m_dirtyRegion += dirtyRegion.intersected(m_boundingRectMax); } qCDebug(lcRenderable) << "addDirtyRegion: " << dirtyRegion << "old dirtyRegion: " << prev << "new dirtyRegion: " << m_dirtyRegion; } @@ -407,7 +424,7 @@ void QSGSoftwareRenderableNode::subtractDirtyRegion(const QRegion &dirtyRegion) QRegion prev = m_dirtyRegion; if (m_isDirty) { // Check if this rect concerns us - if (dirtyRegion.intersects(QRegion(boundingRect()))) { + if (dirtyRegion.intersects(m_boundingRectMax)) { m_dirtyRegion -= dirtyRegion; if (m_dirtyRegion.isEmpty()) m_isDirty = false; @@ -423,7 +440,7 @@ QRegion QSGSoftwareRenderableNode::previousDirtyRegion(bool wasRemoved) const if (wasRemoved) return m_previousDirtyRegion; - return m_previousDirtyRegion.subtracted(QRegion(m_boundingRect)); + return m_previousDirtyRegion.subtracted(QRegion(m_boundingRectMax)); } QRegion QSGSoftwareRenderableNode::dirtyRegion() const diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h index 473578c185..8fc87db179 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h @@ -98,7 +98,8 @@ public: void update(); QRegion renderNode(QPainter *painter, bool forceOpaquePainting = false); - QRect boundingRect() const; + QRect boundingRectMin() const { return m_boundingRectMin; } + QRect boundingRectMax() const { return m_boundingRectMax; } NodeType type() const { return m_nodeType; } bool isOpaque() const { return m_isOpaque; } bool isDirty() const { return m_isDirty; } @@ -149,7 +150,8 @@ private: bool m_hasClipRegion; float m_opacity; - QRect m_boundingRect; + QRect m_boundingRectMin; + QRect m_boundingRectMax; }; QT_END_NAMESPACE -- cgit v1.2.3 From a5fbfc9ab1dc06c9f80beda2179b15bd7c499c77 Mon Sep 17 00:00:00 2001 From: Jesus Fernandez Date: Mon, 22 May 2017 09:49:18 +0200 Subject: Document the default value of x, y, width & height The default values of the coordinates and size variables were missing. Change-Id: I1bba77b3db68d3f726c1211c6a0090e97f687b51 Reviewed-by: Mitch Curtis Reviewed-by: Shawn Rutledge --- src/quick/items/qquickitem.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 8e90827a3d..5f5611d711 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -6343,6 +6343,7 @@ void QQuickItem::setFlags(Flags flags) \qmlproperty real QtQuick::Item::height Defines the item's position and size. + The default value is \c 0. The (x,y) position is relative to the \l parent. -- cgit v1.2.3 From c158ca8be49a75026e83751dfd825c5bdd63189a Mon Sep 17 00:00:00 2001 From: Pier Luigi Fiorini Date: Sat, 31 Dec 2016 00:07:50 +0100 Subject: Add screen product information Add information such as manufacturer, model and serial number that is now available on QScreen to the Screen attached property. [ChangeLog][QtQuick][Screen] Add manufacturer, model and serial number. Change-Id: If8f33dffa5eff33111f93212249424b9092250b8 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickscreen.cpp | 48 ++++++++++++++++++++++++++++++++++ src/quick/items/qquickscreen_p.h | 9 +++++++ src/quick/items/qquickwindowmodule.cpp | 1 + 3 files changed, 58 insertions(+) (limited to 'src') diff --git a/src/quick/items/qquickscreen.cpp b/src/quick/items/qquickscreen.cpp index 9b54b7fba9..6a3eab957e 100644 --- a/src/quick/items/qquickscreen.cpp +++ b/src/quick/items/qquickscreen.cpp @@ -99,6 +99,27 @@ QT_BEGIN_NAMESPACE The y coordinate of the screen within the virtual desktop. */ +/*! + \qmlattachedproperty string Screen::manufacturer + \readonly + \since 5.10 + + The manufacturer of the screen. +*/ +/*! + \qmlattachedproperty string Screen::model + \readonly + \since 5.10 + + The model of the screen. +*/ +/*! + \qmlattachedproperty string Screen::serialNumber + \readonly + \since 5.10 + + The serial number of the screen. +*/ /*! \qmlattachedproperty int Screen::width \readonly @@ -234,6 +255,27 @@ QString QQuickScreenInfo::name() const return m_screen->name(); } +QString QQuickScreenInfo::manufacturer() const +{ + if (!m_screen) + return QString(); + return m_screen->manufacturer(); +} + +QString QQuickScreenInfo::model() const +{ + if (!m_screen) + return QString(); + return m_screen->model(); +} + +QString QQuickScreenInfo::serialNumber() const +{ + if (!m_screen) + return QString(); + return m_screen->serialNumber(); +} + int QQuickScreenInfo::width() const { if (!m_screen) @@ -335,6 +377,12 @@ void QQuickScreenInfo::setWrappedScreen(QScreen *screen) } if (!oldScreen || screen->name() != oldScreen->name()) emit nameChanged(); + if (!oldScreen || screen->manufacturer() != oldScreen->manufacturer()) + emit manufacturerChanged(); + if (!oldScreen || screen->model() != oldScreen->model()) + emit modelChanged(); + if (!oldScreen || screen->serialNumber() != oldScreen->serialNumber()) + emit serialNumberChanged(); if (!oldScreen || screen->orientation() != oldScreen->orientation()) emit orientationChanged(); if (!oldScreen || screen->primaryOrientation() != oldScreen->primaryOrientation()) diff --git a/src/quick/items/qquickscreen_p.h b/src/quick/items/qquickscreen_p.h index 99e1466631..e9db07d14c 100644 --- a/src/quick/items/qquickscreen_p.h +++ b/src/quick/items/qquickscreen_p.h @@ -68,6 +68,9 @@ class Q_AUTOTEST_EXPORT QQuickScreenInfo : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name NOTIFY nameChanged) + Q_PROPERTY(QString manufacturer READ manufacturer NOTIFY manufacturerChanged REVISION 10) + Q_PROPERTY(QString model READ model NOTIFY modelChanged REVISION 10) + Q_PROPERTY(QString serialNumber READ serialNumber NOTIFY serialNumberChanged REVISION 10) Q_PROPERTY(int width READ width NOTIFY widthChanged) Q_PROPERTY(int height READ height NOTIFY heightChanged) Q_PROPERTY(int desktopAvailableWidth READ desktopAvailableWidth NOTIFY desktopGeometryChanged) @@ -87,6 +90,9 @@ public: QQuickScreenInfo(QObject *parent = nullptr, QScreen *wrappedScreen = nullptr); QString name() const; + QString manufacturer() const; + QString model() const; + QString serialNumber() const; int width() const; int height() const; int desktopAvailableWidth() const; @@ -104,6 +110,9 @@ public: Q_SIGNALS: void nameChanged(); + Q_REVISION(10) void manufacturerChanged(); + Q_REVISION(10) void modelChanged(); + Q_REVISION(10) void serialNumberChanged(); void widthChanged(); void heightChanged(); void desktopGeometryChanged(); diff --git a/src/quick/items/qquickwindowmodule.cpp b/src/quick/items/qquickwindowmodule.cpp index a5234d4f77..a7f45534c4 100644 --- a/src/quick/items/qquickwindowmodule.cpp +++ b/src/quick/items/qquickwindowmodule.cpp @@ -200,6 +200,7 @@ void QQuickWindowModule::defineModule() qmlRegisterUncreatableType(uri, 2, 0, "Screen", QStringLiteral("Screen can only be used via the attached property.")); qmlRegisterUncreatableType(uri, 2, 3, "Screen", QStringLiteral("Screen can only be used via the attached property.")); qmlRegisterUncreatableType(uri, 2, 3, "ScreenInfo", QStringLiteral("ScreenInfo can only be used via the attached property.")); + qmlRegisterUncreatableType(uri, 2, 10, "ScreenInfo", QStringLiteral("ScreenInfo can only be used via the attached property.")); } QT_END_NAMESPACE -- cgit v1.2.3 From df30d90eadcd92eebb4bf79f3eb251bc58d25d09 Mon Sep 17 00:00:00 2001 From: Liang Qi Date: Tue, 23 May 2017 17:09:32 +0200 Subject: Revert "Fix compilation of QtScxml" This reverts commit 3f9367cb32533b691cb8c761213f21a524e3d1cb. QtScxml adapted to new API. Task-number: QTBUG-60938 Change-Id: I417b604ceb9949ee3c0197ac9f88efdbe53ebe48 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4string_p.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'src') diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index d9625e3f4e..f5311ae5d4 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -175,10 +175,6 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed { } uint toUInt(bool *ok) const; - Q_DECL_DEPRECATED void makeIdentifier(ExecutionEngine *) { - makeIdentifier(); - } - void makeIdentifier() const { if (d()->identifier) return; -- cgit v1.2.3 From 44a5b008f7a6dce065f5997503e403609ee62859 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 23 May 2017 14:34:23 +0200 Subject: Forward ShortcutOverride in QQuickWidget This is now essential since otherwise these events are simply lost. Amends 0dbc575c1a8359534761167a5f5f1e29abedd51d Task-number: QTBUG-60988 Change-Id: Ib1d99d8fcd5bb92c9b52977796f2910f0fe71c48 Reviewed-by: J-P Nurmi Reviewed-by: Andy Nichols --- src/quickwidgets/qquickwidget.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index 49ac0caefd..2e8623f508 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -1440,6 +1440,9 @@ bool QQuickWidget::event(QEvent *e) d->offscreenWindow->setWindowState(resolveWindowState(windowState())); break; + case QEvent::ShortcutOverride: + return QCoreApplication::sendEvent(d->offscreenWindow, e); + default: break; } -- cgit v1.2.3 From 06c0702e7e252227ca907bc3654179ad079cd1b5 Mon Sep 17 00:00:00 2001 From: Stephan Binner Date: Sat, 6 May 2017 23:04:03 +0200 Subject: Add sections and update purposes of features Change-Id: I95923fa183172bb4fe5d0a6ae34c801e0cee2a63 Reviewed-by: Oswald Buddenhagen Reviewed-by: Shawn Rutledge --- src/qml/configure.json | 9 ++++++--- src/quick/configure.json | 41 +++++++++++++++++++++++++++-------------- 2 files changed, 33 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/qml/configure.json b/src/qml/configure.json index 2c4887365f..257bedecbc 100644 --- a/src/qml/configure.json +++ b/src/qml/configure.json @@ -15,17 +15,20 @@ "features": { "qml-interpreter": { "label": "QML interpreter", - "purpose": "Support for the QML interpreter", + "purpose": "Provides the QML interpreter.", + "section": "QML", "output": [ "privateFeature" ] }, "qml-network": { "label": "QML network support", - "purpose": "Provides network transparency for QML", + "purpose": "Provides network transparency.", + "section": "QML", "output": [ "publicFeature" ] }, "qml-profiler": { "label": "Command line QML Profiler", - "purpose": "The QML Profiler retrieves QML tracing data from an application.", + "purpose": "Supports retrieving QML tracing data from an application.", + "section": "QML", "condition": [ "features.commandlineparser", "features.localserver", diff --git a/src/quick/configure.json b/src/quick/configure.json index 047fa8c948..65ad5b810b 100644 --- a/src/quick/configure.json +++ b/src/quick/configure.json @@ -34,7 +34,8 @@ "features": { "d3d12": { "label": "Direct3D 12", - "purpose": "Provides a Direct3D 12 backend for the Qt Quick Scenegraph", + "purpose": "Provides a Direct3D 12 backend for the scenegraph.", + "section": "Qt Quick", "condition": "tests.d3d12", "output": [ "publicFeature" @@ -42,7 +43,8 @@ }, "quick-animatedimage": { "label": "AnimatedImage item", - "purpose": "Provides the Qt Quick AnimatedImage Item", + "purpose": "Provides the AnimatedImage item.", + "section": "Qt Quick", "condition": "features.movie", "output": [ "privateFeature" @@ -50,29 +52,33 @@ }, "quick-canvas": { "label": "Canvas item", - "purpose": "Provides the Qt Quick Canvas Item", + "purpose": "Provides the Canvas item.", + "section": "Qt Quick", "condition": "features.quick-path", "output": [ "privateFeature" ] }, "quick-designer": { - "label": "Support for Quick Designer", - "purpose": "Provides support for the Qt Quick Designer in Qt Creator", + "label": "Support for Qt Quick Designer", + "purpose": "Provides support for the Qt Quick Designer in Qt Creator.", + "section": "Qt Quick", "output": [ "privateFeature" ] }, "quick-flipable": { "label": "Flipable item", - "purpose": "Provides the Qt Quick Flipable Item", + "purpose": "Provides the Flipable item.", + "section": "Qt Quick", "output": [ "privateFeature" ] }, "quick-gridview": { "label": "GridView item", - "purpose": "Provides the Qt Quick GridView item", + "purpose": "Provides the GridView item.", + "section": "Qt Quick", "output": [ "privateFeature" ] @@ -93,14 +99,16 @@ }, "quick-listview": { "label": "ListView item", - "purpose": "Provides the Qt Quick ListView item", + "purpose": "Provides the ListView item.", + "section": "Qt Quick", "output": [ "privateFeature" ] }, "quick-particles": { "label": "Particle support", - "purpose": "Provides a particle system for Qt Quick", + "purpose": "Provides a particle system.", + "section": "Qt Quick", "condition": "features.quick-shadereffect && features.quick-sprite && features.opengl", "output": [ "privateFeature" @@ -108,14 +116,16 @@ }, "quick-path": { "label": "Path support", - "purpose": "Provides Path elements in Qt Quick", + "purpose": "Provides Path elements.", + "section": "Qt Quick", "output": [ "privateFeature" ] }, "quick-pathview": { "label": "PathView item", - "purpose": "Provides the Qt Quick PathView item", + "purpose": "Provides the PathView item.", + "section": "Qt Quick", "condition": "features.quick-path", "output": [ "privateFeature" @@ -123,21 +133,24 @@ }, "quick-positioners": { "label": "Positioner items", - "purpose": "Provides Positioner items in Qt Quick", + "purpose": "Provides Positioner items.", + "section": "Qt Quick", "output": [ "privateFeature" ] }, "quick-shadereffect": { "label": "ShaderEffect item", - "purpose": "Provides Shader effects in Qt Quick", + "purpose": "Provides Shader effects.", + "section": "Qt Quick", "output": [ "privateFeature" ] }, "quick-sprite": { "label": "Sprite item", - "purpose": "Provides the Qt Quick Sprite Item", + "purpose": "Provides the Sprite item.", + "section": "Qt Quick", "output": [ "privateFeature" ] -- cgit v1.2.3 From 651a4f7e094f844cac3740744d148102bbf7689f Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Thu, 1 Jun 2017 13:16:28 +0200 Subject: Revert "QQuickRectangle: Optimize setGradient" This reverts commit 9709d04ba7787c853a1ddbeed0347eab27c0924f. We changed this from using a signal for notification to using parenting. But this cannot be done for two related reasons: the first: QtObject { property Gradient someGradient: Gradient { id: someGradient } } ... Rectangle { gradient: someGradient } ... should work, and no longer did, because the Gradient's parent was the QtObject, not the Rectangle it was tied to. And secondly: Rectangle { id: rectOne gradient: Gradient { id: someGradient } } Rectangle { id: rectTwo gradient: someGradient } ... also didn't work anymore, as the Gradient was only notifying a single Item about changes to it. The easiest way to fix this is to just revert back to using a signal connection. This also caused problems in qtquickcontrols autotests. Change-Id: Ic07dd02e9920596d0c047bfc23995b3be8c96c49 Task-number: QTBUG-60268 Reviewed-by: J-P Nurmi --- src/quick/items/qquickrectangle.cpp | 19 +++++++++++++++++-- src/quick/items/qquickrectangle_p.h | 6 ++++++ src/quick/items/qquickrectangle_p_p.h | 1 + 3 files changed, 24 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickrectangle.cpp b/src/quick/items/qquickrectangle.cpp index 0c532bcd4c..65284eb532 100644 --- a/src/quick/items/qquickrectangle.cpp +++ b/src/quick/items/qquickrectangle.cpp @@ -1,6 +1,5 @@ /**************************************************************************** ** -** Copyright (C) 2017 Crimson AS ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** @@ -270,9 +269,11 @@ QGradientStops QQuickGradient::gradientStops() const void QQuickGradient::doUpdate() { - static_cast(parent())->update(); + emit updated(); } +int QQuickRectanglePrivate::doUpdateSlotIdx = -1; + /*! \qmltype Rectangle \instantiates QQuickRectangle @@ -326,6 +327,11 @@ QQuickRectangle::QQuickRectangle(QQuickItem *parent) setFlag(ItemHasContents); } +void QQuickRectangle::doUpdate() +{ + update(); +} + /*! \qmlproperty bool QtQuick::Rectangle::antialiasing @@ -390,7 +396,16 @@ void QQuickRectangle::setGradient(QQuickGradient *gradient) Q_D(QQuickRectangle); if (d->gradient == gradient) return; + static int updatedSignalIdx = -1; + if (updatedSignalIdx < 0) + updatedSignalIdx = QMetaMethod::fromSignal(&QQuickGradient::updated).methodIndex(); + if (d->doUpdateSlotIdx < 0) + d->doUpdateSlotIdx = QQuickRectangle::staticMetaObject.indexOfSlot("doUpdate()"); + if (d->gradient) + QMetaObject::disconnect(d->gradient, updatedSignalIdx, this, d->doUpdateSlotIdx); d->gradient = gradient; + if (d->gradient) + QMetaObject::connect(d->gradient, updatedSignalIdx, this, d->doUpdateSlotIdx); update(); } diff --git a/src/quick/items/qquickrectangle_p.h b/src/quick/items/qquickrectangle_p.h index 627f778e44..724a06013c 100644 --- a/src/quick/items/qquickrectangle_p.h +++ b/src/quick/items/qquickrectangle_p.h @@ -129,6 +129,9 @@ public: QGradientStops gradientStops() const; +Q_SIGNALS: + void updated(); + private: void doUpdate(); @@ -169,6 +172,9 @@ Q_SIGNALS: protected: QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE; +private Q_SLOTS: + void doUpdate(); + private: Q_DISABLE_COPY(QQuickRectangle) Q_DECLARE_PRIVATE(QQuickRectangle) diff --git a/src/quick/items/qquickrectangle_p_p.h b/src/quick/items/qquickrectangle_p_p.h index e771beec87..3c1aaf7661 100644 --- a/src/quick/items/qquickrectangle_p_p.h +++ b/src/quick/items/qquickrectangle_p_p.h @@ -76,6 +76,7 @@ public: QQuickGradient *gradient; QQuickPen *pen; qreal radius; + static int doUpdateSlotIdx; }; QT_END_NAMESPACE -- cgit v1.2.3 From ac02a71a9cb8e80014218ba7de46f4f914b6e00c Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Tue, 30 May 2017 09:11:28 +0200 Subject: Revert "QQuickWindow::createTextureFromImage(): return nullptr for null images" This reverts commit e6acf80136db9f667d0d4664f6c68065355d6811. This breaks behavioral compatibility. Task-number: QTBUG-61083 Change-Id: I0161d536502bab31aaf4ebc38f91e6c8842f72b0 Reviewed-by: Laszlo Agocs --- src/quick/items/qquickwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index ff4a357641..c441cfc357 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -3862,7 +3862,7 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image, CreateTextureOptions options) const { Q_D(const QQuickWindow); - if (!isSceneGraphInitialized() || image.isNull()) // check both for d->context and d->context->isValid() + if (!isSceneGraphInitialized()) // check both for d->context and d->context->isValid() return 0; uint flags = 0; if (options & TextureCanUseAtlas) flags |= QSGRenderContext::CreateTexture_Atlas; -- cgit v1.2.3 From 7ab6f0dda1cbe4ffd154226996482bc6b092690b Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Wed, 31 May 2017 16:05:46 +0200 Subject: Remove QQuickAnimatedImage::sourceSizeChanged Thanks to moc improvements now the Q_PROPERTY can use the signal defined in the parent Change-Id: Ib69a1b985e7d972cf3a787d22f0403b63b330c36 Reviewed-by: Mitch Curtis --- src/quick/items/qquickanimatedimage_p.h | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickanimatedimage_p.h b/src/quick/items/qquickanimatedimage_p.h index 143fe8904d..54da093259 100644 --- a/src/quick/items/qquickanimatedimage_p.h +++ b/src/quick/items/qquickanimatedimage_p.h @@ -97,7 +97,6 @@ Q_SIGNALS: void playingChanged(); void pausedChanged(); void frameChanged(); - void sourceSizeChanged(); private Q_SLOTS: void movieUpdate(); -- cgit v1.2.3 From 961da5273e17655e73ec0975c6de446b88d7f5ca Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 31 May 2017 12:42:09 +0200 Subject: Rename to Shape/ShapePath and remove public JS API Change-Id: I299354da0632fb0b8487cfb13748ed58b97d75fd Reviewed-by: Andy Nichols --- src/imports/imports.pro | 2 +- src/imports/pathitem/pathitem.pro | 31 - src/imports/pathitem/plugin.cpp | 74 - src/imports/pathitem/plugins.qmltypes | 312 --- src/imports/pathitem/qmldir | 4 - src/imports/pathitem/qquicknvprfunctions.cpp | 284 --- src/imports/pathitem/qquicknvprfunctions_p.h | 406 ---- src/imports/pathitem/qquicknvprfunctions_p_p.h | 70 - src/imports/pathitem/qquickpathitem.cpp | 2258 -------------------- src/imports/pathitem/qquickpathitem_p.h | 333 --- src/imports/pathitem/qquickpathitem_p_p.h | 282 --- .../pathitem/qquickpathitemgenericrenderer.cpp | 775 ------- .../pathitem/qquickpathitemgenericrenderer_p.h | 303 --- .../pathitem/qquickpathitemnvprrenderer.cpp | 923 -------- .../pathitem/qquickpathitemnvprrenderer_p.h | 237 -- .../pathitem/qquickpathitemsoftwarerenderer.cpp | 277 --- .../pathitem/qquickpathitemsoftwarerenderer_p.h | 136 -- src/imports/shapes/plugin.cpp | 74 + src/imports/shapes/plugins.qmltypes | 292 +++ src/imports/shapes/qmldir | 4 + src/imports/shapes/qquicknvprfunctions.cpp | 277 +++ src/imports/shapes/qquicknvprfunctions_p.h | 400 ++++ src/imports/shapes/qquicknvprfunctions_p_p.h | 70 + src/imports/shapes/qquickshape.cpp | 1485 +++++++++++++ src/imports/shapes/qquickshape_p.h | 327 +++ src/imports/shapes/qquickshape_p_p.h | 282 +++ src/imports/shapes/qquickshapegenericrenderer.cpp | 775 +++++++ src/imports/shapes/qquickshapegenericrenderer_p.h | 303 +++ src/imports/shapes/qquickshapenvprrenderer.cpp | 923 ++++++++ src/imports/shapes/qquickshapenvprrenderer_p.h | 237 ++ src/imports/shapes/qquickshapesoftwarerenderer.cpp | 277 +++ src/imports/shapes/qquickshapesoftwarerenderer_p.h | 136 ++ src/imports/shapes/shapes.pro | 31 + src/quick/util/qquickpath.cpp | 16 +- 34 files changed, 5902 insertions(+), 6714 deletions(-) delete mode 100644 src/imports/pathitem/pathitem.pro delete mode 100644 src/imports/pathitem/plugin.cpp delete mode 100644 src/imports/pathitem/plugins.qmltypes delete mode 100644 src/imports/pathitem/qmldir delete mode 100644 src/imports/pathitem/qquicknvprfunctions.cpp delete mode 100644 src/imports/pathitem/qquicknvprfunctions_p.h delete mode 100644 src/imports/pathitem/qquicknvprfunctions_p_p.h delete mode 100644 src/imports/pathitem/qquickpathitem.cpp delete mode 100644 src/imports/pathitem/qquickpathitem_p.h delete mode 100644 src/imports/pathitem/qquickpathitem_p_p.h delete mode 100644 src/imports/pathitem/qquickpathitemgenericrenderer.cpp delete mode 100644 src/imports/pathitem/qquickpathitemgenericrenderer_p.h delete mode 100644 src/imports/pathitem/qquickpathitemnvprrenderer.cpp delete mode 100644 src/imports/pathitem/qquickpathitemnvprrenderer_p.h delete mode 100644 src/imports/pathitem/qquickpathitemsoftwarerenderer.cpp delete mode 100644 src/imports/pathitem/qquickpathitemsoftwarerenderer_p.h create mode 100644 src/imports/shapes/plugin.cpp create mode 100644 src/imports/shapes/plugins.qmltypes create mode 100644 src/imports/shapes/qmldir create mode 100644 src/imports/shapes/qquicknvprfunctions.cpp create mode 100644 src/imports/shapes/qquicknvprfunctions_p.h create mode 100644 src/imports/shapes/qquicknvprfunctions_p_p.h create mode 100644 src/imports/shapes/qquickshape.cpp create mode 100644 src/imports/shapes/qquickshape_p.h create mode 100644 src/imports/shapes/qquickshape_p_p.h create mode 100644 src/imports/shapes/qquickshapegenericrenderer.cpp create mode 100644 src/imports/shapes/qquickshapegenericrenderer_p.h create mode 100644 src/imports/shapes/qquickshapenvprrenderer.cpp create mode 100644 src/imports/shapes/qquickshapenvprrenderer_p.h create mode 100644 src/imports/shapes/qquickshapesoftwarerenderer.cpp create mode 100644 src/imports/shapes/qquickshapesoftwarerenderer_p.h create mode 100644 src/imports/shapes/shapes.pro (limited to 'src') diff --git a/src/imports/imports.pro b/src/imports/imports.pro index df0ad01c06..3c7f96eff9 100644 --- a/src/imports/imports.pro +++ b/src/imports/imports.pro @@ -23,7 +23,7 @@ qtHaveModule(quick) { qtConfig(quick-particles): \ SUBDIRS += particles - SUBDIRS += pathitem + SUBDIRS += shapes } qtHaveModule(xmlpatterns) : SUBDIRS += xmllistmodel diff --git a/src/imports/pathitem/pathitem.pro b/src/imports/pathitem/pathitem.pro deleted file mode 100644 index d70bb6f203..0000000000 --- a/src/imports/pathitem/pathitem.pro +++ /dev/null @@ -1,31 +0,0 @@ -CXX_MODULE = qml -TARGET = qmlpathitemplugin -TARGETPATH = Qt/labs/pathitem -IMPORT_VERSION = 1.0 - -QT = core gui qml quick quick-private - -HEADERS += \ - qquickpathitem_p.h \ - qquickpathitem_p_p.h \ - qquickpathitemgenericrenderer_p.h \ - qquickpathitemsoftwarerenderer_p.h - -SOURCES += \ - plugin.cpp \ - qquickpathitem.cpp \ - qquickpathitemgenericrenderer.cpp \ - qquickpathitemsoftwarerenderer.cpp - -qtConfig(opengl) { - HEADERS += \ - qquicknvprfunctions_p.h \ - qquicknvprfunctions_p_p.h \ - qquickpathitemnvprrenderer_p.h - - SOURCES += \ - qquicknvprfunctions.cpp \ - qquickpathitemnvprrenderer.cpp -} - -load(qml_plugin) diff --git a/src/imports/pathitem/plugin.cpp b/src/imports/pathitem/plugin.cpp deleted file mode 100644 index 6b43e8f398..0000000000 --- a/src/imports/pathitem/plugin.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include - -#include "qquickpathitem_p.h" - -static void initResources() -{ -#ifdef QT_STATIC - Q_INIT_RESOURCE(qmake_Qt_labs_pathitem); -#endif -} - -QT_BEGIN_NAMESPACE - -class QmlPathItemPlugin : public QQmlExtensionPlugin -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) - -public: - QmlPathItemPlugin(QObject *parent = 0) : QQmlExtensionPlugin(parent) { initResources(); } - void registerTypes(const char *uri) Q_DECL_OVERRIDE - { - Q_ASSERT(QByteArray(uri) == QByteArray("Qt.labs.pathitem")); - qmlRegisterType(uri, 1, 0, "PathItem"); - qmlRegisterType(uri, 1, 0, "VisualPath"); - qmlRegisterType(uri, 1, 0, "PathGradientStop"); - qmlRegisterUncreatableType(uri, 1, 0, "PathGradient", QQuickPathGradient::tr("PathGradient is an abstract base class")); - qmlRegisterType(uri, 1, 0, "PathLinearGradient"); - } -}; - -QT_END_NAMESPACE - -#include "plugin.moc" diff --git a/src/imports/pathitem/plugins.qmltypes b/src/imports/pathitem/plugins.qmltypes deleted file mode 100644 index 03f26e243c..0000000000 --- a/src/imports/pathitem/plugins.qmltypes +++ /dev/null @@ -1,312 +0,0 @@ -import QtQuick.tooling 1.2 - -// This file describes the plugin-supplied types contained in the library. -// It is used for QML tooling purposes only. -// -// This file was auto-generated by: -// 'qmlplugindump -nonrelocatable -noforceqtquick Qt.labs.pathitem 1.0' - -Module { - dependencies: [] - Component { - name: "QQuickItem" - defaultProperty: "data" - prototype: "QObject" - Enum { - name: "TransformOrigin" - values: { - "TopLeft": 0, - "Top": 1, - "TopRight": 2, - "Left": 3, - "Center": 4, - "Right": 5, - "BottomLeft": 6, - "Bottom": 7, - "BottomRight": 8 - } - } - Property { name: "parent"; type: "QQuickItem"; isPointer: true } - Property { name: "data"; type: "QObject"; isList: true; isReadonly: true } - Property { name: "resources"; type: "QObject"; isList: true; isReadonly: true } - Property { name: "children"; type: "QQuickItem"; isList: true; isReadonly: true } - Property { name: "x"; type: "double" } - Property { name: "y"; type: "double" } - Property { name: "z"; type: "double" } - Property { name: "width"; type: "double" } - Property { name: "height"; type: "double" } - Property { name: "opacity"; type: "double" } - Property { name: "enabled"; type: "bool" } - Property { name: "visible"; type: "bool" } - Property { name: "visibleChildren"; type: "QQuickItem"; isList: true; isReadonly: true } - Property { name: "states"; type: "QQuickState"; isList: true; isReadonly: true } - Property { name: "transitions"; type: "QQuickTransition"; isList: true; isReadonly: true } - Property { name: "state"; type: "string" } - Property { name: "childrenRect"; type: "QRectF"; isReadonly: true } - Property { name: "anchors"; type: "QQuickAnchors"; isReadonly: true; isPointer: true } - Property { name: "left"; type: "QQuickAnchorLine"; isReadonly: true } - Property { name: "right"; type: "QQuickAnchorLine"; isReadonly: true } - Property { name: "horizontalCenter"; type: "QQuickAnchorLine"; isReadonly: true } - Property { name: "top"; type: "QQuickAnchorLine"; isReadonly: true } - Property { name: "bottom"; type: "QQuickAnchorLine"; isReadonly: true } - Property { name: "verticalCenter"; type: "QQuickAnchorLine"; isReadonly: true } - Property { name: "baseline"; type: "QQuickAnchorLine"; isReadonly: true } - Property { name: "baselineOffset"; type: "double" } - Property { name: "clip"; type: "bool" } - Property { name: "focus"; type: "bool" } - Property { name: "activeFocus"; type: "bool"; isReadonly: true } - Property { name: "activeFocusOnTab"; revision: 1; type: "bool" } - Property { name: "rotation"; type: "double" } - Property { name: "scale"; type: "double" } - Property { name: "transformOrigin"; type: "TransformOrigin" } - Property { name: "transformOriginPoint"; type: "QPointF"; isReadonly: true } - Property { name: "transform"; type: "QQuickTransform"; isList: true; isReadonly: true } - Property { name: "smooth"; type: "bool" } - Property { name: "antialiasing"; type: "bool" } - Property { name: "implicitWidth"; type: "double" } - Property { name: "implicitHeight"; type: "double" } - Property { name: "layer"; type: "QQuickItemLayer"; isReadonly: true; isPointer: true } - Signal { - name: "childrenRectChanged" - Parameter { type: "QRectF" } - } - Signal { - name: "baselineOffsetChanged" - Parameter { type: "double" } - } - Signal { - name: "stateChanged" - Parameter { type: "string" } - } - Signal { - name: "focusChanged" - Parameter { type: "bool" } - } - Signal { - name: "activeFocusChanged" - Parameter { type: "bool" } - } - Signal { - name: "activeFocusOnTabChanged" - revision: 1 - Parameter { type: "bool" } - } - Signal { - name: "parentChanged" - Parameter { type: "QQuickItem"; isPointer: true } - } - Signal { - name: "transformOriginChanged" - Parameter { type: "TransformOrigin" } - } - Signal { - name: "smoothChanged" - Parameter { type: "bool" } - } - Signal { - name: "antialiasingChanged" - Parameter { type: "bool" } - } - Signal { - name: "clipChanged" - Parameter { type: "bool" } - } - Signal { - name: "windowChanged" - revision: 1 - Parameter { name: "window"; type: "QQuickWindow"; isPointer: true } - } - Method { name: "update" } - Method { - name: "grabToImage" - revision: 2 - type: "bool" - Parameter { name: "callback"; type: "QJSValue" } - Parameter { name: "targetSize"; type: "QSize" } - } - Method { - name: "grabToImage" - revision: 2 - type: "bool" - Parameter { name: "callback"; type: "QJSValue" } - } - Method { - name: "contains" - type: "bool" - Parameter { name: "point"; type: "QPointF" } - } - Method { - name: "mapFromItem" - Parameter { type: "QQmlV4Function"; isPointer: true } - } - Method { - name: "mapToItem" - Parameter { type: "QQmlV4Function"; isPointer: true } - } - Method { - name: "mapFromGlobal" - revision: 7 - Parameter { type: "QQmlV4Function"; isPointer: true } - } - Method { - name: "mapToGlobal" - revision: 7 - Parameter { type: "QQmlV4Function"; isPointer: true } - } - Method { name: "forceActiveFocus" } - Method { - name: "forceActiveFocus" - Parameter { name: "reason"; type: "Qt::FocusReason" } - } - Method { - name: "nextItemInFocusChain" - revision: 1 - type: "QQuickItem*" - Parameter { name: "forward"; type: "bool" } - } - Method { name: "nextItemInFocusChain"; revision: 1; type: "QQuickItem*" } - Method { - name: "childAt" - type: "QQuickItem*" - Parameter { name: "x"; type: "double" } - Parameter { name: "y"; type: "double" } - } - } - Component { - name: "QQuickPathGradient" - defaultProperty: "stops" - prototype: "QObject" - exports: ["Qt.labs.pathitem/PathGradient 1.0"] - isCreatable: false - exportMetaObjectRevisions: [0] - Enum { - name: "SpreadMode" - values: { - "PadSpread": 0, - "RepeatSpread": 1, - "ReflectSpread": 2 - } - } - Property { name: "stops"; type: "QObject"; isList: true; isReadonly: true } - Property { name: "spread"; type: "SpreadMode" } - Signal { name: "updated" } - } - Component { - name: "QQuickPathGradientStop" - prototype: "QObject" - exports: ["Qt.labs.pathitem/PathGradientStop 1.0"] - exportMetaObjectRevisions: [0] - Property { name: "position"; type: "double" } - Property { name: "color"; type: "QColor" } - } - Component { - name: "QQuickPathItem" - defaultProperty: "elements" - prototype: "QQuickItem" - exports: ["Qt.labs.pathitem/PathItem 1.0"] - exportMetaObjectRevisions: [0] - Enum { - name: "RendererType" - values: { - "UnknownRenderer": 0, - "GeometryRenderer": 1, - "NvprRenderer": 2, - "SoftwareRenderer": 3 - } - } - Enum { - name: "Status" - values: { - "Null": 0, - "Ready": 1, - "Processing": 2 - } - } - Property { name: "renderer"; type: "RendererType"; isReadonly: true } - Property { name: "asynchronous"; type: "bool" } - Property { name: "enableVendorExtensions"; type: "bool" } - Property { name: "status"; type: "Status"; isReadonly: true } - Property { name: "elements"; type: "QQuickVisualPath"; isList: true; isReadonly: true } - Method { - name: "newPath" - Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true } - } - Method { - name: "newStrokeFillParams" - Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true } - } - Method { - name: "clearVisualPaths" - Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true } - } - Method { - name: "commitVisualPaths" - Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true } - } - Method { - name: "appendVisualPath" - Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true } - } - } - Component { - name: "QQuickPathLinearGradient" - defaultProperty: "stops" - prototype: "QQuickPathGradient" - exports: ["Qt.labs.pathitem/PathLinearGradient 1.0"] - exportMetaObjectRevisions: [0] - Property { name: "x1"; type: "double" } - Property { name: "y1"; type: "double" } - Property { name: "x2"; type: "double" } - Property { name: "y2"; type: "double" } - } - Component { - name: "QQuickVisualPath" - defaultProperty: "path" - prototype: "QObject" - exports: ["Qt.labs.pathitem/VisualPath 1.0"] - exportMetaObjectRevisions: [0] - Enum { - name: "FillRule" - values: { - "OddEvenFill": 0, - "WindingFill": 1 - } - } - Enum { - name: "JoinStyle" - values: { - "MiterJoin": 0, - "BevelJoin": 64, - "RoundJoin": 128 - } - } - Enum { - name: "CapStyle" - values: { - "FlatCap": 0, - "SquareCap": 16, - "RoundCap": 32 - } - } - Enum { - name: "StrokeStyle" - values: { - "SolidLine": 1, - "DashLine": 2 - } - } - Property { name: "path"; type: "QQuickPath"; isPointer: true } - Property { name: "strokeColor"; type: "QColor" } - Property { name: "strokeWidth"; type: "double" } - Property { name: "fillColor"; type: "QColor" } - Property { name: "fillRule"; type: "FillRule" } - Property { name: "joinStyle"; type: "JoinStyle" } - Property { name: "miterLimit"; type: "int" } - Property { name: "capStyle"; type: "CapStyle" } - Property { name: "strokeStyle"; type: "StrokeStyle" } - Property { name: "dashOffset"; type: "double" } - Property { name: "dashPattern"; type: "QVector" } - Property { name: "fillGradient"; type: "QQuickPathGradient"; isPointer: true } - Signal { name: "changed" } - } -} diff --git a/src/imports/pathitem/qmldir b/src/imports/pathitem/qmldir deleted file mode 100644 index 277b8a199b..0000000000 --- a/src/imports/pathitem/qmldir +++ /dev/null @@ -1,4 +0,0 @@ -module Qt.labs.pathitem -plugin qmlpathitemplugin -classname QmlPathItemPlugin -typeinfo plugins.qmltypes diff --git a/src/imports/pathitem/qquicknvprfunctions.cpp b/src/imports/pathitem/qquicknvprfunctions.cpp deleted file mode 100644 index 40eb2bb932..0000000000 --- a/src/imports/pathitem/qquicknvprfunctions.cpp +++ /dev/null @@ -1,284 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquicknvprfunctions_p.h" - -#ifndef QT_NO_OPENGL - -#include -#include -#include -#include "qquicknvprfunctions_p_p.h" - -QT_BEGIN_NAMESPACE - -/*! - \class QQuickNvprFunctions - - \brief Function resolvers and other helpers for GL_NV_path_rendering - for both desktop (GL 4.3+) and mobile/embedded (GLES 3.1+) in a manner - that does not distract builds that do not have NVPR support either at - compile or run time. - - \internal - */ - -QQuickNvprFunctions::QQuickNvprFunctions() - : d(new QQuickNvprFunctionsPrivate(this)) -{ -} - -QQuickNvprFunctions::~QQuickNvprFunctions() -{ - delete d; -} - -/*! - \return a recommended QSurfaceFormat suitable for GL_NV_path_rendering on top - of OpenGL 4.3 or OpenGL ES 3.1. - */ -QSurfaceFormat QQuickNvprFunctions::format() -{ - QSurfaceFormat fmt; - fmt.setDepthBufferSize(24); - fmt.setStencilBufferSize(8); - if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { - fmt.setVersion(4, 3); - fmt.setProfile(QSurfaceFormat::CompatibilityProfile); - } else if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { - fmt.setVersion(3, 1); - } - return fmt; -} - -/*! - \return true if GL_NV_path_rendering is supported with the current OpenGL - context. - - When there is no current context, a temporary dummy one will be created and - made current. - */ -bool QQuickNvprFunctions::isSupported() -{ - QOpenGLContext *ctx = QOpenGLContext::currentContext(); - QScopedPointer tempContext; - QScopedPointer tempSurface; - if (!ctx) { - tempContext.reset(new QOpenGLContext); - if (!tempContext->create()) - return false; - ctx = tempContext.data(); - tempSurface.reset(new QOffscreenSurface); - tempSurface->setFormat(ctx->format()); - tempSurface->create(); - if (!ctx->makeCurrent(tempSurface.data())) - return false; - } - - if (!ctx->hasExtension(QByteArrayLiteral("GL_NV_path_rendering"))) - return false; - - // Do not check for DSA as the string may not be exposed on ES - // drivers, yet the functions we need are resolvable. -#if 0 - if (!ctx->hasExtension(QByteArrayLiteral("GL_EXT_direct_state_access"))) { - qWarning("QtQuickPath/NVPR: GL_EXT_direct_state_access not supported"); - return false; - } -#endif - - return true; -} - -/*! - Initializes using the current OpenGL context. - - \return true when GL_NV_path_rendering is supported and initialization was - successful. - */ -bool QQuickNvprFunctions::create() -{ - return isSupported() && d->resolve(); -} - -/*! - Creates a program pipeline consisting of a separable fragment shader program. - - This is essential for using NVPR with OpenGL ES 3.1+ since normal, - GLES2-style programs would not work without a vertex shader. - - \note \a fragmentShaderSource should be a \c{version 310 es} shader since - this works both on desktop and embedded NVIDIA drivers, thus avoiding the - need to fight GLSL and GLSL ES differences. - - The pipeline object is stored into \a pipeline, the fragment shader program - into \a program. - - Use QOpenGLExtraFunctions to set uniforms, bind the pipeline, etc. - - \return \c false on failure in which case the error log is printed on the - debug output. \c true on success. - */ -bool QQuickNvprFunctions::createFragmentOnlyPipeline(const char *fragmentShaderSource, GLuint *pipeline, GLuint *program) -{ - QOpenGLContext *ctx = QOpenGLContext::currentContext(); - if (!ctx) - return false; - - QOpenGLExtraFunctions *f = ctx->extraFunctions(); - *program = f->glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &fragmentShaderSource); - GLint status = 0; - f->glGetProgramiv(*program, GL_LINK_STATUS, &status); - if (!status) { - GLint len = 0; - f->glGetProgramiv(*program, GL_INFO_LOG_LENGTH, &len); - if (len) { - QByteArray s; - s.resize(len); - f->glGetProgramInfoLog(*program, s.count(), nullptr, s.data()); - qWarning("Failed to create separable shader program:\n%s", s.constData()); - } - return false; - } - - f->glGenProgramPipelines(1, pipeline); - f->glUseProgramStages(*pipeline, GL_FRAGMENT_SHADER_BIT, *program); - f->glActiveShaderProgram(*pipeline, *program); - - f->glValidateProgramPipeline(*pipeline); - status = 0; - f->glGetProgramPipelineiv(*pipeline, GL_VALIDATE_STATUS, &status); - if (!status) { - GLint len = 0; - f->glGetProgramPipelineiv(*pipeline, GL_INFO_LOG_LENGTH, &len); - if (len) { - QByteArray s; - s.resize(len); - f->glGetProgramPipelineInfoLog(*pipeline, s.count(), nullptr, s.data()); - qWarning("Program pipeline validation failed:\n%s", s.constData()); - } - return false; - } - - return true; -} - -#define PROC(type, name) reinterpret_cast(ctx->getProcAddress(#name)) - -bool QQuickNvprFunctionsPrivate::resolve() -{ - QOpenGLContext *ctx = QOpenGLContext::currentContext(); - - q->genPaths = PROC(PFNGLGENPATHSNVPROC, glGenPathsNV); - q->deletePaths = PROC(PFNGLDELETEPATHSNVPROC, glDeletePathsNV); - q->isPath = PROC(PFNGLISPATHNVPROC, glIsPathNV); - q->pathCommands = PROC(PFNGLPATHCOMMANDSNVPROC, glPathCommandsNV); - q->pathCoords = PROC(PFNGLPATHCOORDSNVPROC, glPathCoordsNV); - q->pathSubCommands = PROC(PFNGLPATHSUBCOMMANDSNVPROC, glPathSubCommandsNV); - q->pathSubCoords = PROC(PFNGLPATHSUBCOORDSNVPROC, glPathSubCoordsNV); - q->pathString = PROC(PFNGLPATHSTRINGNVPROC, glPathStringNV); - q->pathGlyphs = PROC(PFNGLPATHGLYPHSNVPROC, glPathGlyphsNV); - q->pathGlyphRange = PROC(PFNGLPATHGLYPHRANGENVPROC, glPathGlyphRangeNV); - q->weightPaths = PROC(PFNGLWEIGHTPATHSNVPROC, glWeightPathsNV); - q->copyPath = PROC(PFNGLCOPYPATHNVPROC, glCopyPathNV); - q->interpolatePaths = PROC(PFNGLINTERPOLATEPATHSNVPROC, glInterpolatePathsNV); - q->transformPath = PROC(PFNGLTRANSFORMPATHNVPROC, glTransformPathNV); - q->pathParameteriv = PROC(PFNGLPATHPARAMETERIVNVPROC, glPathParameterivNV); - q->pathParameteri = PROC(PFNGLPATHPARAMETERINVPROC, glPathParameteriNV); - q->pathParameterfv = PROC(PFNGLPATHPARAMETERFVNVPROC, glPathParameterfvNV); - q->pathParameterf = PROC(PFNGLPATHPARAMETERFNVPROC, glPathParameterfNV); - q->pathDashArray = PROC(PFNGLPATHDASHARRAYNVPROC, glPathDashArrayNV); - q->pathStencilFunc = PROC(PFNGLPATHSTENCILFUNCNVPROC, glPathStencilFuncNV); - q->pathStencilDepthOffset = PROC(PFNGLPATHSTENCILDEPTHOFFSETNVPROC, glPathStencilDepthOffsetNV); - q->stencilFillPath = PROC(PFNGLSTENCILFILLPATHNVPROC, glStencilFillPathNV); - q->stencilStrokePath = PROC(PFNGLSTENCILSTROKEPATHNVPROC, glStencilStrokePathNV); - q->stencilFillPathInstanced = PROC(PFNGLSTENCILFILLPATHINSTANCEDNVPROC, glStencilFillPathInstancedNV); - q->stencilStrokePathInstanced = PROC(PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC, glStencilStrokePathInstancedNV); - q->pathCoverDepthFunc = PROC(PFNGLPATHCOVERDEPTHFUNCNVPROC, glPathCoverDepthFuncNV); - q->pathColorGen = PROC(PFNGLPATHCOLORGENNVPROC, glPathColorGenNV); - q->pathTexGen = PROC(PFNGLPATHTEXGENNVPROC, glPathTexGenNV); - q->pathFogGen = PROC(PFNGLPATHFOGGENNVPROC, glPathFogGenNV); - q->coverFillPath = PROC(PFNGLCOVERFILLPATHNVPROC, glCoverFillPathNV); - q->coverStrokePath = PROC(PFNGLCOVERSTROKEPATHNVPROC, glCoverStrokePathNV); - q->coverFillPathInstanced = PROC(PFNGLCOVERFILLPATHINSTANCEDNVPROC, glCoverFillPathInstancedNV); - q->coverStrokePathInstanced = PROC(PFNGLCOVERSTROKEPATHINSTANCEDNVPROC, glCoverStrokePathInstancedNV); - q->getPathParameteriv = PROC(PFNGLGETPATHPARAMETERIVNVPROC, glGetPathParameterivNV); - q->getPathParameterfv = PROC(PFNGLGETPATHPARAMETERFVNVPROC, glGetPathParameterfvNV); - q->getPathCommands = PROC(PFNGLGETPATHCOMMANDSNVPROC, glGetPathCommandsNV); - q->getPathCoords = PROC(PFNGLGETPATHCOORDSNVPROC, glGetPathCoordsNV); - q->getPathDashArray = PROC(PFNGLGETPATHDASHARRAYNVPROC, glGetPathDashArrayNV); - q->getPathMetrics = PROC(PFNGLGETPATHMETRICSNVPROC, glGetPathMetricsNV); - q->getPathMetricRange = PROC(PFNGLGETPATHMETRICRANGENVPROC, glGetPathMetricRangeNV); - q->getPathSpacing = PROC(PFNGLGETPATHSPACINGNVPROC, glGetPathSpacingNV); - q->getPathColorgeniv = PROC(PFNGLGETPATHCOLORGENIVNVPROC, glGetPathColorGenivNV); - q->getPathColorgenfv = PROC(PFNGLGETPATHCOLORGENFVNVPROC, glGetPathColorGenfvNV); - q->getPathTexGeniv = PROC(PFNGLGETPATHTEXGENIVNVPROC, glGetPathTexGenivNV); - q->getPathTexGenfv = PROC(PFNGLGETPATHTEXGENFVNVPROC, glGetPathTexGenfvNV); - q->isPointInFillPath = PROC(PFNGLISPOINTINFILLPATHNVPROC, glIsPointInFillPathNV); - q->isPointInStrokePath = PROC(PFNGLISPOINTINSTROKEPATHNVPROC, glIsPointInStrokePathNV); - q->getPathLength = PROC(PFNGLGETPATHLENGTHNVPROC, glGetPathLengthNV); - q->getPointAlongPath = PROC(PFNGLPOINTALONGPATHNVPROC, glPointAlongPathNV); - q->matrixLoad3x2f = PROC(PFNGLMATRIXLOAD3X2FNVPROC, glMatrixLoad3x2fNV); - q->matrixLoad3x3f = PROC(PFNGLMATRIXLOAD3X3FNVPROC, glMatrixLoad3x3fNV); - q->matrixLoadTranspose3x3f = PROC(PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC, glMatrixLoadTranspose3x3fNV); - q->matrixMult3x2f = PROC(PFNGLMATRIXMULT3X2FNVPROC, glMatrixMult3x2fNV); - q->matrixMult3x3f = PROC(PFNGLMATRIXMULT3X3FNVPROC, glMatrixMult3x3fNV); - q->matrixMultTranspose3x3f = PROC(PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC, glMatrixMultTranspose3x3fNV); - q->stencilThenCoverFillPath = PROC(PFNGLSTENCILTHENCOVERFILLPATHNVPROC, glStencilThenCoverFillPathNV); - q->stencilThenCoverStrokePath = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC, glStencilThenCoverStrokePathNV); - q->stencilThenCoverFillPathInstanced = PROC(PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC, glStencilThenCoverFillPathInstancedNV); - q->stencilThenCoverStrokePathInstanced = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC, glStencilThenCoverStrokePathInstancedNV); - q->pathGlyphIndexRange = PROC(PFNGLPATHGLYPHINDEXRANGENVPROC, glPathGlyphIndexRangeNV); - q->pathGlyphIndexArray = PROC(PFNGLPATHGLYPHINDEXARRAYNVPROC, glPathGlyphIndexArrayNV); - q->pathMemoryGlyphIndexArray = PROC(PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC, glPathMemoryGlyphIndexArrayNV); - q->programPathFragmentInputGen = PROC(PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC, glProgramPathFragmentInputGenNV); - q->getProgramResourcefv = PROC(PFNGLGETPROGRAMRESOURCEFVNVPROC, glGetProgramResourcefvNV); - - q->matrixLoadf = PROC(PFNGLMATRIXLOADFEXTPROC, glMatrixLoadfEXT); - q->matrixLoadIdentity = PROC(PFNGLMATRIXLOADIDENTITYEXTPROC, glMatrixLoadIdentityEXT); - - return q->genPaths != nullptr // base path rendering ext - && q->programPathFragmentInputGen != nullptr // updated path rendering ext - && q->matrixLoadf != nullptr // direct state access ext - && q->matrixLoadIdentity != nullptr; -} - -QT_END_NAMESPACE - -#endif // QT_NO_OPENGL diff --git a/src/imports/pathitem/qquicknvprfunctions_p.h b/src/imports/pathitem/qquicknvprfunctions_p.h deleted file mode 100644 index 7900388305..0000000000 --- a/src/imports/pathitem/qquicknvprfunctions_p.h +++ /dev/null @@ -1,406 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKNVPRFUNCTIONS_P_H -#define QQUICKNVPRFUNCTIONS_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of a number of Qt sources files. This header file may change from -// version to version without notice, or even be removed. -// -// We mean it. -// - -#include -#include - -#ifndef QT_NO_OPENGL - -QT_BEGIN_NAMESPACE - -#ifndef GL_NV_path_rendering -#define GL_PATH_FORMAT_SVG_NV 0x9070 -#define GL_PATH_FORMAT_PS_NV 0x9071 -#define GL_STANDARD_FONT_NAME_NV 0x9072 -#define GL_SYSTEM_FONT_NAME_NV 0x9073 -#define GL_FILE_NAME_NV 0x9074 -#define GL_PATH_STROKE_WIDTH_NV 0x9075 -#define GL_PATH_END_CAPS_NV 0x9076 -#define GL_PATH_INITIAL_END_CAP_NV 0x9077 -#define GL_PATH_TERMINAL_END_CAP_NV 0x9078 -#define GL_PATH_JOIN_STYLE_NV 0x9079 -#define GL_PATH_MITER_LIMIT_NV 0x907A -#define GL_PATH_DASH_CAPS_NV 0x907B -#define GL_PATH_INITIAL_DASH_CAP_NV 0x907C -#define GL_PATH_TERMINAL_DASH_CAP_NV 0x907D -#define GL_PATH_DASH_OFFSET_NV 0x907E -#define GL_PATH_CLIENT_LENGTH_NV 0x907F -#define GL_PATH_FILL_MODE_NV 0x9080 -#define GL_PATH_FILL_MASK_NV 0x9081 -#define GL_PATH_FILL_COVER_MODE_NV 0x9082 -#define GL_PATH_STROKE_COVER_MODE_NV 0x9083 -#define GL_PATH_STROKE_MASK_NV 0x9084 -#define GL_COUNT_UP_NV 0x9088 -#define GL_COUNT_DOWN_NV 0x9089 -#define GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A -#define GL_CONVEX_HULL_NV 0x908B -#define GL_BOUNDING_BOX_NV 0x908D -#define GL_TRANSLATE_X_NV 0x908E -#define GL_TRANSLATE_Y_NV 0x908F -#define GL_TRANSLATE_2D_NV 0x9090 -#define GL_TRANSLATE_3D_NV 0x9091 -#define GL_AFFINE_2D_NV 0x9092 -#define GL_AFFINE_3D_NV 0x9094 -#define GL_TRANSPOSE_AFFINE_2D_NV 0x9096 -#define GL_TRANSPOSE_AFFINE_3D_NV 0x9098 -#define GL_UTF8_NV 0x909A -#define GL_UTF16_NV 0x909B -#define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C -#define GL_PATH_COMMAND_COUNT_NV 0x909D -#define GL_PATH_COORD_COUNT_NV 0x909E -#define GL_PATH_DASH_ARRAY_COUNT_NV 0x909F -#define GL_PATH_COMPUTED_LENGTH_NV 0x90A0 -#define GL_PATH_FILL_BOUNDING_BOX_NV 0x90A1 -#define GL_PATH_STROKE_BOUNDING_BOX_NV 0x90A2 -#define GL_SQUARE_NV 0x90A3 -#define GL_ROUND_NV 0x90A4 -#define GL_TRIANGULAR_NV 0x90A5 -#define GL_BEVEL_NV 0x90A6 -#define GL_MITER_REVERT_NV 0x90A7 -#define GL_MITER_TRUNCATE_NV 0x90A8 -#define GL_SKIP_MISSING_GLYPH_NV 0x90A9 -#define GL_USE_MISSING_GLYPH_NV 0x90AA -#define GL_PATH_ERROR_POSITION_NV 0x90AB -#define GL_PATH_FOG_GEN_MODE_NV 0x90AC -#define GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD -#define GL_ADJACENT_PAIRS_NV 0x90AE -#define GL_FIRST_TO_REST_NV 0x90AF -#define GL_PATH_GEN_MODE_NV 0x90B0 -#define GL_PATH_GEN_COEFF_NV 0x90B1 -#define GL_PATH_GEN_COLOR_FORMAT_NV 0x90B2 -#define GL_PATH_GEN_COMPONENTS_NV 0x90B3 -#define GL_PATH_STENCIL_FUNC_NV 0x90B7 -#define GL_PATH_STENCIL_REF_NV 0x90B8 -#define GL_PATH_STENCIL_VALUE_MASK_NV 0x90B9 -#define GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD -#define GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE -#define GL_PATH_COVER_DEPTH_FUNC_NV 0x90BF -#define GL_PATH_DASH_OFFSET_RESET_NV 0x90B4 -#define GL_MOVE_TO_RESETS_NV 0x90B5 -#define GL_MOVE_TO_CONTINUES_NV 0x90B6 -#define GL_CLOSE_PATH_NV 0x00 -#define GL_MOVE_TO_NV 0x02 -#define GL_RELATIVE_MOVE_TO_NV 0x03 -#define GL_LINE_TO_NV 0x04 -#define GL_RELATIVE_LINE_TO_NV 0x05 -#define GL_HORIZONTAL_LINE_TO_NV 0x06 -#define GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07 -#define GL_VERTICAL_LINE_TO_NV 0x08 -#define GL_RELATIVE_VERTICAL_LINE_TO_NV 0x09 -#define GL_QUADRATIC_CURVE_TO_NV 0x0A -#define GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B -#define GL_CUBIC_CURVE_TO_NV 0x0C -#define GL_RELATIVE_CUBIC_CURVE_TO_NV 0x0D -#define GL_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E -#define GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F -#define GL_SMOOTH_CUBIC_CURVE_TO_NV 0x10 -#define GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11 -#define GL_SMALL_CCW_ARC_TO_NV 0x12 -#define GL_RELATIVE_SMALL_CCW_ARC_TO_NV 0x13 -#define GL_SMALL_CW_ARC_TO_NV 0x14 -#define GL_RELATIVE_SMALL_CW_ARC_TO_NV 0x15 -#define GL_LARGE_CCW_ARC_TO_NV 0x16 -#define GL_RELATIVE_LARGE_CCW_ARC_TO_NV 0x17 -#define GL_LARGE_CW_ARC_TO_NV 0x18 -#define GL_RELATIVE_LARGE_CW_ARC_TO_NV 0x19 -#define GL_RESTART_PATH_NV 0xF0 -#define GL_DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2 -#define GL_DUP_LAST_CUBIC_CURVE_TO_NV 0xF4 -#define GL_RECT_NV 0xF6 -#define GL_CIRCULAR_CCW_ARC_TO_NV 0xF8 -#define GL_CIRCULAR_CW_ARC_TO_NV 0xFA -#define GL_CIRCULAR_TANGENT_ARC_TO_NV 0xFC -#define GL_ARC_TO_NV 0xFE -#define GL_RELATIVE_ARC_TO_NV 0xFF -#define GL_BOLD_BIT_NV 0x01 -#define GL_ITALIC_BIT_NV 0x02 -#define GL_GLYPH_WIDTH_BIT_NV 0x01 -#define GL_GLYPH_HEIGHT_BIT_NV 0x02 -#define GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04 -#define GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08 -#define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10 -#define GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20 -#define GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40 -#define GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80 -#define GL_GLYPH_HAS_KERNING_BIT_NV 0x100 -#define GL_FONT_X_MIN_BOUNDS_BIT_NV 0x00010000 -#define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000 -#define GL_FONT_X_MAX_BOUNDS_BIT_NV 0x00040000 -#define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000 -#define GL_FONT_UNITS_PER_EM_BIT_NV 0x00100000 -#define GL_FONT_ASCENDER_BIT_NV 0x00200000 -#define GL_FONT_DESCENDER_BIT_NV 0x00400000 -#define GL_FONT_HEIGHT_BIT_NV 0x00800000 -#define GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000 -#define GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000 -#define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000 -#define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000 -#define GL_FONT_HAS_KERNING_BIT_NV 0x10000000 -#define GL_PRIMARY_COLOR_NV 0x852C -#define GL_SECONDARY_COLOR_NV 0x852D -#define GL_ROUNDED_RECT_NV 0xE8 -#define GL_RELATIVE_ROUNDED_RECT_NV 0xE9 -#define GL_ROUNDED_RECT2_NV 0xEA -#define GL_RELATIVE_ROUNDED_RECT2_NV 0xEB -#define GL_ROUNDED_RECT4_NV 0xEC -#define GL_RELATIVE_ROUNDED_RECT4_NV 0xED -#define GL_ROUNDED_RECT8_NV 0xEE -#define GL_RELATIVE_ROUNDED_RECT8_NV 0xEF -#define GL_RELATIVE_RECT_NV 0xF7 -#define GL_FONT_GLYPHS_AVAILABLE_NV 0x9368 -#define GL_FONT_TARGET_UNAVAILABLE_NV 0x9369 -#define GL_FONT_UNAVAILABLE_NV 0x936A -#define GL_FONT_UNINTELLIGIBLE_NV 0x936B -#define GL_CONIC_CURVE_TO_NV 0x1A -#define GL_RELATIVE_CONIC_CURVE_TO_NV 0x1B -#define GL_FONT_NUM_GLYPH_INDICES_BIT_NV 0x20000000 -#define GL_STANDARD_FONT_FORMAT_NV 0x936C -#define GL_2_BYTES_NV 0x1407 -#define GL_3_BYTES_NV 0x1408 -#define GL_4_BYTES_NV 0x1409 -#define GL_EYE_LINEAR_NV 0x2400 -#define GL_OBJECT_LINEAR_NV 0x2401 -#define GL_CONSTANT_NV 0x8576 -#define GL_PATH_PROJECTION_NV 0x1701 -#define GL_PATH_MODELVIEW_NV 0x1700 -#define GL_PATH_MODELVIEW_STACK_DEPTH_NV 0x0BA3 -#define GL_PATH_MODELVIEW_MATRIX_NV 0x0BA6 -#define GL_PATH_MAX_MODELVIEW_STACK_DEPTH_NV 0x0D36 -#define GL_PATH_TRANSPOSE_MODELVIEW_MATRIX_NV 0x84E3 -#define GL_PATH_PROJECTION_STACK_DEPTH_NV 0x0BA4 -#define GL_PATH_PROJECTION_MATRIX_NV 0x0BA7 -#define GL_PATH_MAX_PROJECTION_STACK_DEPTH_NV 0x0D38 -#define GL_PATH_TRANSPOSE_PROJECTION_MATRIX_NV 0x84E4 -#define GL_FRAGMENT_INPUT_NV 0x936D - -typedef GLuint (QOPENGLF_APIENTRYP PFNGLGENPATHSNVPROC) (GLsizei range); -typedef void (QOPENGLF_APIENTRYP PFNGLDELETEPATHSNVPROC) (GLuint path, GLsizei range); -typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPATHNVPROC) (GLuint path); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOMMANDSNVPROC) (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOORDSNVPROC) (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHSUBCOMMANDSNVPROC) (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHSUBCOORDSNVPROC) (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTRINGNVPROC) (GLuint path, GLenum format, GLsizei length, const void *pathString); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHGLYPHSNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHGLYPHRANGENVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); -typedef void (QOPENGLF_APIENTRYP PFNGLWEIGHTPATHSNVPROC) (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); -typedef void (QOPENGLF_APIENTRYP PFNGLCOPYPATHNVPROC) (GLuint resultPath, GLuint srcPath); -typedef void (QOPENGLF_APIENTRYP PFNGLINTERPOLATEPATHSNVPROC) (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); -typedef void (QOPENGLF_APIENTRYP PFNGLTRANSFORMPATHNVPROC) (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, const GLint *value); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERINVPROC) (GLuint path, GLenum pname, GLint value); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, const GLfloat *value); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERFNVPROC) (GLuint path, GLenum pname, GLfloat value); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHDASHARRAYNVPROC) (GLuint path, GLsizei dashCount, const GLfloat *dashArray); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTENCILFUNCNVPROC) (GLenum func, GLint ref, GLuint mask); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTENCILDEPTHOFFSETNVPROC) (GLfloat factor, GLfloat units); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOVERDEPTHFUNCNVPROC) (GLenum func); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOLORGENNVPROC) (GLenum color, GLenum genMode, GLenum colorFormat, const GLfloat *coeffs); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHTEXGENNVPROC) (GLenum texCoordSet, GLenum genMode, GLint components, const GLfloat *coeffs); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHFOGGENNVPROC) (GLenum genMode); -typedef void (QOPENGLF_APIENTRYP PFNGLCOVERFILLPATHNVPROC) (GLuint path, GLenum coverMode); -typedef void (QOPENGLF_APIENTRYP PFNGLCOVERSTROKEPATHNVPROC) (GLuint path, GLenum coverMode); -typedef void (QOPENGLF_APIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); -typedef void (QOPENGLF_APIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, GLint *value); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, GLfloat *value); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOMMANDSNVPROC) (GLuint path, GLubyte *commands); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOORDSNVPROC) (GLuint path, GLfloat *coords); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHDASHARRAYNVPROC) (GLuint path, GLfloat *dashArray); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHMETRICSNVPROC) (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHMETRICRANGENVPROC) (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHSPACINGNVPROC) (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOLORGENIVNVPROC) (GLenum color, GLenum pname, GLint *value); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOLORGENFVNVPROC) (GLenum color, GLenum pname, GLfloat *value); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHTEXGENIVNVPROC) (GLenum texCoordSet, GLenum pname, GLint *value); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHTEXGENFVNVPROC) (GLenum texCoordSet, GLenum pname, GLfloat *value); -typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPOINTINFILLPATHNVPROC) (GLuint path, GLuint mask, GLfloat x, GLfloat y); -typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPOINTINSTROKEPATHNVPROC) (GLuint path, GLfloat x, GLfloat y); -typedef GLfloat (QOPENGLF_APIENTRYP PFNGLGETPATHLENGTHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments); -typedef GLboolean (QOPENGLF_APIENTRYP PFNGLPOINTALONGPATHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOAD3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOAD3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULT3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULT3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask, GLenum coverMode); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); -typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHGLYPHINDEXRANGENVPROC) (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint baseAndCount[2]); -typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); -typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); -typedef void (QOPENGLF_APIENTRYP PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC) (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPROGRAMRESOURCEFVNVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLfloat *params); -#endif - -#ifndef GL_FLAT -#define GL_FLAT 0x1D00 -#endif - -#ifndef GL_INVERT -#define GL_INVERT 0x150A -#endif - -#ifndef GL_EXT_direct_state_access -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADFEXTPROC) (GLenum mode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC) (GLenum mode); -#endif - -// When building on a system with GLES 2.0 or 3.0, we may still compile the NVPR -// code path even though it's never used. Keep it compiling by defining the -// necessary ES 3.1 separable program constants. -#ifndef GL_FRAGMENT_SHADER_BIT -#define GL_FRAGMENT_SHADER_BIT 0x00000002 -#endif -#ifndef GL_UNIFORM -#define GL_UNIFORM 0x92E1 -#endif - -class QQuickNvprFunctionsPrivate; - -class QQuickNvprFunctions -{ -public: - QQuickNvprFunctions(); - ~QQuickNvprFunctions(); - - static QSurfaceFormat format(); - static bool isSupported(); - - bool create(); - - bool createFragmentOnlyPipeline(const char *fragmentShaderSource, GLuint *pipeline, GLuint *program); - - PFNGLGENPATHSNVPROC genPaths = nullptr; - PFNGLDELETEPATHSNVPROC deletePaths = nullptr; - PFNGLISPATHNVPROC isPath = nullptr; - PFNGLPATHCOMMANDSNVPROC pathCommands = nullptr; - PFNGLPATHCOORDSNVPROC pathCoords = nullptr; - PFNGLPATHSUBCOMMANDSNVPROC pathSubCommands = nullptr; - PFNGLPATHSUBCOORDSNVPROC pathSubCoords = nullptr; - PFNGLPATHSTRINGNVPROC pathString = nullptr; - PFNGLPATHGLYPHSNVPROC pathGlyphs = nullptr; - PFNGLPATHGLYPHRANGENVPROC pathGlyphRange = nullptr; - PFNGLWEIGHTPATHSNVPROC weightPaths = nullptr; - PFNGLCOPYPATHNVPROC copyPath = nullptr; - PFNGLINTERPOLATEPATHSNVPROC interpolatePaths = nullptr; - PFNGLTRANSFORMPATHNVPROC transformPath = nullptr; - PFNGLPATHPARAMETERIVNVPROC pathParameteriv = nullptr; - PFNGLPATHPARAMETERINVPROC pathParameteri = nullptr; - PFNGLPATHPARAMETERFVNVPROC pathParameterfv = nullptr; - PFNGLPATHPARAMETERFNVPROC pathParameterf = nullptr; - PFNGLPATHDASHARRAYNVPROC pathDashArray = nullptr; - PFNGLPATHSTENCILFUNCNVPROC pathStencilFunc = nullptr; - PFNGLPATHSTENCILDEPTHOFFSETNVPROC pathStencilDepthOffset = nullptr; - PFNGLSTENCILFILLPATHNVPROC stencilFillPath = nullptr; - PFNGLSTENCILSTROKEPATHNVPROC stencilStrokePath = nullptr; - PFNGLSTENCILFILLPATHINSTANCEDNVPROC stencilFillPathInstanced = nullptr; - PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC stencilStrokePathInstanced = nullptr; - PFNGLPATHCOVERDEPTHFUNCNVPROC pathCoverDepthFunc = nullptr; - PFNGLPATHCOLORGENNVPROC pathColorGen = nullptr; - PFNGLPATHTEXGENNVPROC pathTexGen = nullptr; - PFNGLPATHFOGGENNVPROC pathFogGen = nullptr; - PFNGLCOVERFILLPATHNVPROC coverFillPath = nullptr; - PFNGLCOVERSTROKEPATHNVPROC coverStrokePath = nullptr; - PFNGLCOVERFILLPATHINSTANCEDNVPROC coverFillPathInstanced = nullptr; - PFNGLCOVERSTROKEPATHINSTANCEDNVPROC coverStrokePathInstanced = nullptr; - PFNGLGETPATHPARAMETERIVNVPROC getPathParameteriv = nullptr; - PFNGLGETPATHPARAMETERFVNVPROC getPathParameterfv = nullptr; - PFNGLGETPATHCOMMANDSNVPROC getPathCommands = nullptr; - PFNGLGETPATHCOORDSNVPROC getPathCoords = nullptr; - PFNGLGETPATHDASHARRAYNVPROC getPathDashArray = nullptr; - PFNGLGETPATHMETRICSNVPROC getPathMetrics = nullptr; - PFNGLGETPATHMETRICRANGENVPROC getPathMetricRange = nullptr; - PFNGLGETPATHSPACINGNVPROC getPathSpacing = nullptr; - PFNGLGETPATHCOLORGENIVNVPROC getPathColorgeniv = nullptr; - PFNGLGETPATHCOLORGENFVNVPROC getPathColorgenfv = nullptr; - PFNGLGETPATHTEXGENIVNVPROC getPathTexGeniv = nullptr; - PFNGLGETPATHTEXGENFVNVPROC getPathTexGenfv = nullptr; - PFNGLISPOINTINFILLPATHNVPROC isPointInFillPath = nullptr; - PFNGLISPOINTINSTROKEPATHNVPROC isPointInStrokePath = nullptr; - PFNGLGETPATHLENGTHNVPROC getPathLength = nullptr; - PFNGLPOINTALONGPATHNVPROC getPointAlongPath = nullptr; - PFNGLMATRIXLOAD3X2FNVPROC matrixLoad3x2f = nullptr; - PFNGLMATRIXLOAD3X3FNVPROC matrixLoad3x3f = nullptr; - PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC matrixLoadTranspose3x3f = nullptr; - PFNGLMATRIXMULT3X2FNVPROC matrixMult3x2f = nullptr; - PFNGLMATRIXMULT3X3FNVPROC matrixMult3x3f = nullptr; - PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC matrixMultTranspose3x3f = nullptr; - PFNGLSTENCILTHENCOVERFILLPATHNVPROC stencilThenCoverFillPath = nullptr; - PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC stencilThenCoverStrokePath = nullptr; - PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC stencilThenCoverFillPathInstanced = nullptr; - PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC stencilThenCoverStrokePathInstanced = nullptr; - PFNGLPATHGLYPHINDEXRANGENVPROC pathGlyphIndexRange = nullptr; - PFNGLPATHGLYPHINDEXARRAYNVPROC pathGlyphIndexArray = nullptr; - PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC pathMemoryGlyphIndexArray = nullptr; - PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC programPathFragmentInputGen = nullptr; - PFNGLGETPROGRAMRESOURCEFVNVPROC getProgramResourcefv = nullptr; - - PFNGLMATRIXLOADFEXTPROC matrixLoadf = nullptr; - PFNGLMATRIXLOADIDENTITYEXTPROC matrixLoadIdentity = nullptr; - -private: - QQuickNvprFunctionsPrivate *d; -}; - -QT_END_NAMESPACE - -#endif // QT_NO_OPENGL - -#endif // QQUICKNVPRFUNCTIONS_P_H diff --git a/src/imports/pathitem/qquicknvprfunctions_p_p.h b/src/imports/pathitem/qquicknvprfunctions_p_p.h deleted file mode 100644 index 6df20566af..0000000000 --- a/src/imports/pathitem/qquicknvprfunctions_p_p.h +++ /dev/null @@ -1,70 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKNVPRFUNCTIONS_P_P_H -#define QQUICKNVPRFUNCTIONS_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of a number of Qt sources files. This header file may change from -// version to version without notice, or even be removed. -// -// We mean it. -// - -#include "qquicknvprfunctions_p.h" - -QT_BEGIN_NAMESPACE - -class QQuickNvprFunctionsPrivate -{ -public: - QQuickNvprFunctionsPrivate(QQuickNvprFunctions *q_ptr) : q(q_ptr) { } - - bool resolve(); - - QQuickNvprFunctions *q; -}; - -QT_END_NAMESPACE - -#endif // QQUICKNVPRFUNCTIONS_P_P_H diff --git a/src/imports/pathitem/qquickpathitem.cpp b/src/imports/pathitem/qquickpathitem.cpp deleted file mode 100644 index 5255a55798..0000000000 --- a/src/imports/pathitem/qquickpathitem.cpp +++ /dev/null @@ -1,2258 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquickpathitem_p.h" -#include "qquickpathitem_p_p.h" -#include "qquickpathitemgenericrenderer_p.h" -#include "qquickpathitemnvprrenderer_p.h" -#include "qquickpathitemsoftwarerenderer_p.h" -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -QQuickPathItemStrokeFillParams::QQuickPathItemStrokeFillParams() - : strokeColor(Qt::white), - strokeWidth(1), - fillColor(Qt::white), - fillRule(QQuickVisualPath::OddEvenFill), - joinStyle(QQuickVisualPath::BevelJoin), - miterLimit(2), - capStyle(QQuickVisualPath::SquareCap), - strokeStyle(QQuickVisualPath::SolidLine), - dashOffset(0), - fillGradient(nullptr) -{ - dashPattern << 4 << 2; // 4 * strokeWidth dash followed by 2 * strokeWidth space -} - -QPainterPath QQuickPathItemPath::toPainterPath() const -{ - QPainterPath p; - int coordIdx = 0; - for (int i = 0; i < cmd.count(); ++i) { - switch (cmd[i]) { - case QQuickPathItemPath::MoveTo: - p.moveTo(coords[coordIdx], coords[coordIdx + 1]); - coordIdx += 2; - break; - case QQuickPathItemPath::LineTo: - p.lineTo(coords[coordIdx], coords[coordIdx + 1]); - coordIdx += 2; - break; - case QQuickPathItemPath::QuadTo: - p.quadTo(coords[coordIdx], coords[coordIdx + 1], - coords[coordIdx + 2], coords[coordIdx + 3]); - coordIdx += 4; - break; - case QQuickPathItemPath::CubicTo: - p.cubicTo(coords[coordIdx], coords[coordIdx + 1], - coords[coordIdx + 2], coords[coordIdx + 3], - coords[coordIdx + 4], coords[coordIdx + 5]); - coordIdx += 6; - break; - case QQuickPathItemPath::ArcTo: - // does not map to the QPainterPath API; reuse the helper code from QQuickSvgParser - QQuickSvgParser::pathArc(p, - coords[coordIdx], coords[coordIdx + 1], // radius - coords[coordIdx + 2], // xAxisRotation - !qFuzzyIsNull(coords[coordIdx + 6]), // useLargeArc - !qFuzzyIsNull(coords[coordIdx + 5]), // sweep flag - coords[coordIdx + 3], coords[coordIdx + 4], // end - p.currentPosition().x(), p.currentPosition().y()); - coordIdx += 7; - break; - default: - qWarning("Unknown JS path command: %d", cmd[i]); - break; - } - } - return p; -} - -/*! - \qmltype VisualPath - \instantiates QQuickVisualPath - \inqmlmodule QtQuick - \ingroup qtquick-paths - \ingroup qtquick-views - \inherits Object - \brief Describes a Path and associated properties for stroking and filling - \since 5.10 - - A PathItem contains one or more VisualPath elements. At least one - VisualPath is necessary in order to have a PathItem output anything - visible. A VisualPath in turn contains a Path and properties describing the - stroking and filling parameters, such as the stroke width and color, the - fill color or gradient, join and cap styles, and so on. Finally, the Path - object contains a list of path elements like PathMove, PathLine, PathCubic, - PathQuad, PathArc. - - Any property changes in these data sets will be bubble up and change the - output of the PathItem. This means that it is simple and easy to change, or - even animate, the starting and ending position, control points, or any - stroke or fill parameters using the usual QML bindings and animation types - like NumberAnimation. - - In the following example the line join style changes automatically based on - the value of joinStyleIndex: - - \code - VisualPath { - strokeColor: "black" - strokeWidth: 16 - fillColor: "transparent" - capStyle: VisualPath.RoundCap - - property int joinStyleIndex: 0 - property variant styles: [ VisualPath.BevelJoin, VisualPath.MiterJoin, VisualPath.RoundJoin ] - - joinStyle: styles[joinStyleIndex] - - Path { - startX: 30 - startY: 30 - PathLine { x: 100; y: 100 } - PathLine { x: 30; y: 100 } - } - } - \endcode - - Once associated with a PathItem, here is the output with a joinStyleIndex - of 2 (VisualPath.RoundJoin): - - \image visualpath-code-example.png - */ - -QQuickVisualPathPrivate::QQuickVisualPathPrivate() - : path(nullptr), - dirty(DirtyAll) -{ -} - -QQuickVisualPath::QQuickVisualPath(QObject *parent) - : QObject(*(new QQuickVisualPathPrivate), parent) -{ -} - -QQuickVisualPath::~QQuickVisualPath() -{ -} - -/*! - \qmlproperty Path QtQuick::VisualPath::path - - This property holds the Path object. - - \default - */ - -QQuickPath *QQuickVisualPath::path() const -{ - Q_D(const QQuickVisualPath); - return d->path; -} - -void QQuickVisualPath::setPath(QQuickPath *path) -{ - Q_D(QQuickVisualPath); - if (d->path == path) - return; - - if (d->path) - qmlobject_disconnect(d->path, QQuickPath, SIGNAL(changed()), - this, QQuickVisualPath, SLOT(_q_pathChanged())); - d->path = path; - qmlobject_connect(d->path, QQuickPath, SIGNAL(changed()), - this, QQuickVisualPath, SLOT(_q_pathChanged())); - - d->dirty |= QQuickVisualPathPrivate::DirtyPath; - emit pathChanged(); - emit changed(); -} - -void QQuickVisualPathPrivate::_q_pathChanged() -{ - Q_Q(QQuickVisualPath); - dirty |= DirtyPath; - emit q->changed(); -} - -/*! - \qmlproperty color QtQuick::VisualPath::strokeColor - - This property holds the stroking color. - - When set to \c transparent, no stroking occurs. - - The default value is \c white. - */ - -QColor QQuickVisualPath::strokeColor() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.strokeColor; -} - -void QQuickVisualPath::setStrokeColor(const QColor &color) -{ - Q_D(QQuickVisualPath); - if (d->sfp.strokeColor != color) { - d->sfp.strokeColor = color; - d->dirty |= QQuickVisualPathPrivate::DirtyStrokeColor; - emit strokeColorChanged(); - emit changed(); - } -} - -/*! - \qmlproperty color QtQuick::VisualPath::strokeWidth - - This property holds the stroke width. - - When set to a negative value, no stroking occurs. - - The default value is 1. - */ - -qreal QQuickVisualPath::strokeWidth() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.strokeWidth; -} - -void QQuickVisualPath::setStrokeWidth(qreal w) -{ - Q_D(QQuickVisualPath); - if (d->sfp.strokeWidth != w) { - d->sfp.strokeWidth = w; - d->dirty |= QQuickVisualPathPrivate::DirtyStrokeWidth; - emit strokeWidthChanged(); - emit changed(); - } -} - -/*! - \qmlproperty color QtQuick::VisualPath::fillColor - - This property holds the fill color. - - When set to \c transparent, no filling occurs. - - The default value is \c white. - */ - -QColor QQuickVisualPath::fillColor() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.fillColor; -} - -void QQuickVisualPath::setFillColor(const QColor &color) -{ - Q_D(QQuickVisualPath); - if (d->sfp.fillColor != color) { - d->sfp.fillColor = color; - d->dirty |= QQuickVisualPathPrivate::DirtyFillColor; - emit fillColorChanged(); - emit changed(); - } -} - -/*! - \qmlproperty enumeration QtQuick::VisualPath::fillRule - - This property holds the fill rule. The default value is - VisualPath.OddEvenFill. For an example on fill rules, see - QPainterPath::setFillRule(). - - \list - \li VisualPath.OddEvenFill - \li VisualPath.WindingFill - \endlist - */ - -QQuickVisualPath::FillRule QQuickVisualPath::fillRule() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.fillRule; -} - -void QQuickVisualPath::setFillRule(FillRule fillRule) -{ - Q_D(QQuickVisualPath); - if (d->sfp.fillRule != fillRule) { - d->sfp.fillRule = fillRule; - d->dirty |= QQuickVisualPathPrivate::DirtyFillRule; - emit fillRuleChanged(); - emit changed(); - } -} - -/*! - \qmlproperty enumeration QtQuick::VisualPath::joinStyle - - This property defines how joins between two connected lines are drawn. The - default value is VisualPath.BevelJoin. - - \list - \li VisualPath.MiterJoin - The outer edges of the lines are extended to meet at an angle, and this area is filled. - \li VisualPath.BevelJoin - The triangular notch between the two lines is filled. - \li VisualPath.RoundJoin - A circular arc between the two lines is filled. - \endlist - */ - -QQuickVisualPath::JoinStyle QQuickVisualPath::joinStyle() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.joinStyle; -} - -void QQuickVisualPath::setJoinStyle(JoinStyle style) -{ - Q_D(QQuickVisualPath); - if (d->sfp.joinStyle != style) { - d->sfp.joinStyle = style; - d->dirty |= QQuickVisualPathPrivate::DirtyStyle; - emit joinStyleChanged(); - emit changed(); - } -} - -/*! - \qmlproperty int QtQuick::VisualPath::miterLimit - - When VisualPath.joinStyle is set to VisualPath.MiterJoin, this property - specifies how far the miter join can extend from the join point. - - The default value is 2. - */ - -int QQuickVisualPath::miterLimit() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.miterLimit; -} - -void QQuickVisualPath::setMiterLimit(int limit) -{ - Q_D(QQuickVisualPath); - if (d->sfp.miterLimit != limit) { - d->sfp.miterLimit = limit; - d->dirty |= QQuickVisualPathPrivate::DirtyStyle; - emit miterLimitChanged(); - emit changed(); - } -} - -/*! - \qmlproperty enumeration QtQuick::VisualPath::capStyle - - This property defines how the end points of lines are drawn. The - default value is VisualPath.SquareCap. - - \list - \li VisualPath.FlatCap - A square line end that does not cover the end point of the line. - \li VisualPath.SquareCap - A square line end that covers the end point and extends beyond it by half the line width. - \li VisualPath.RoundCap - A rounded line end. - \endlist - */ - -QQuickVisualPath::CapStyle QQuickVisualPath::capStyle() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.capStyle; -} - -void QQuickVisualPath::setCapStyle(CapStyle style) -{ - Q_D(QQuickVisualPath); - if (d->sfp.capStyle != style) { - d->sfp.capStyle = style; - d->dirty |= QQuickVisualPathPrivate::DirtyStyle; - emit capStyleChanged(); - emit changed(); - } -} - -/*! - \qmlproperty enumeration QtQuick::VisualPath::strokeStyle - - This property defines the style of stroking. The default value is - VisualPath.SolidLine. - - \list - \li VisualPath.SolidLine - A plain line. - \li VisualPath.DashLine - Dashes separated by a few pixels. - \endlist - */ - -QQuickVisualPath::StrokeStyle QQuickVisualPath::strokeStyle() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.strokeStyle; -} - -void QQuickVisualPath::setStrokeStyle(StrokeStyle style) -{ - Q_D(QQuickVisualPath); - if (d->sfp.strokeStyle != style) { - d->sfp.strokeStyle = style; - d->dirty |= QQuickVisualPathPrivate::DirtyDash; - emit strokeStyleChanged(); - emit changed(); - } -} - -/*! - \qmlproperty real QtQuick::VisualPath::dashOffset - - This property defines the starting point on the dash pattern, measured in - units used to specify the dash pattern. - - The default value is 0. - - \sa QPen::setDashOffset() - */ - -qreal QQuickVisualPath::dashOffset() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.dashOffset; -} - -void QQuickVisualPath::setDashOffset(qreal offset) -{ - Q_D(QQuickVisualPath); - if (d->sfp.dashOffset != offset) { - d->sfp.dashOffset = offset; - d->dirty |= QQuickVisualPathPrivate::DirtyDash; - emit dashOffsetChanged(); - emit changed(); - } -} - -/*! - \qmlproperty list QtQuick::VisualPath::dashPattern - - This property defines the dash pattern when VisualPath.strokeStyle is set - to VisualPath.DashLine. The pattern must be specified as an even number of - positive entries where the entries 1, 3, 5... are the dashes and 2, 4, 6... - are the spaces. The pattern is specified in units of the pen's width. - - The default value is (4, 2), meaning a dash of 4 * VisualPath.strokeWidth - pixels followed by a space of 2 * VisualPath.strokeWidth pixels. - - \sa QPen::setDashPattern() - */ - -QVector QQuickVisualPath::dashPattern() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.dashPattern; -} - -void QQuickVisualPath::setDashPattern(const QVector &array) -{ - Q_D(QQuickVisualPath); - if (d->sfp.dashPattern != array) { - d->sfp.dashPattern = array; - d->dirty |= QQuickVisualPathPrivate::DirtyDash; - emit dashPatternChanged(); - emit changed(); - } -} - -/*! - \qmlproperty PathGradient QtQuick::VisualPath::fillGradient - - This property defines the fill gradient. By default no gradient is enabled - and the value is \c null. In this case the fill uses a solid color based on - the value of VisuaLPath.fillColor. - - When set, VisualPath.fillColor is ignored and filling is done using one of - the PathGradient subtypes. - */ - -QQuickPathGradient *QQuickVisualPath::fillGradient() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.fillGradient; -} - -void QQuickVisualPath::setFillGradient(QQuickPathGradient *gradient) -{ - Q_D(QQuickVisualPath); - if (d->sfp.fillGradient != gradient) { - if (d->sfp.fillGradient) - qmlobject_disconnect(d->sfp.fillGradient, QQuickPathGradient, SIGNAL(updated()), - this, QQuickVisualPath, SLOT(_q_fillGradientChanged())); - d->sfp.fillGradient = gradient; - if (d->sfp.fillGradient) - qmlobject_connect(d->sfp.fillGradient, QQuickPathGradient, SIGNAL(updated()), - this, QQuickVisualPath, SLOT(_q_fillGradientChanged())); - d->dirty |= QQuickVisualPathPrivate::DirtyFillGradient; - emit changed(); - } -} - -void QQuickVisualPathPrivate::_q_fillGradientChanged() -{ - Q_Q(QQuickVisualPath); - dirty |= DirtyFillGradient; - emit q->changed(); -} - -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 a GPU vendor 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 - PathItem is suitable for creating shapes spreading over larger areas of the - screen, avoiding the performance penalty for texture uploads or framebuffer - blits. In addition, the declarative API allows manipulating, binding to, - and even animating the path element properties like starting and ending - position, the control points, etc. - - 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, PathSvg. - - See \l Path for a detailed overview of the supported path elements. - - \code - PathItem { - width: 200 - height: 150 - anchors.centerIn: parent - VisualPath { - strokeWidth: 4 - strokeColor: "red" - fillGradient: PathLinearGradient { - x1: 20; y1: 20 - x2: 180; y2: 130 - PathGradientStop { position: 0; color: "blue" } - PathGradientStop { position: 0.2; color: "green" } - PathGradientStop { position: 0.4; color: "red" } - PathGradientStop { position: 0.6; color: "yellow" } - PathGradientStop { position: 1; color: "cyan" } - } - strokeStyle: VisualPath.DashLine - dashPattern: [ 1, 4 ] - Path { - startX: 20; startY: 20 - PathLine { x: 180; y: 130 } - PathLine { x: 20; y: 130 } - PathLine { x: 20; y: 20 } - } - } - } - \endcode - - \image pathitem-code-example.png - - \note 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. - - The following list summarizes the available PathItem rendering approaches: - - \list - - \li When running with the default, OpenGL backend of Qt Quick, both the - generic, triangulation-based and the NVIDIA-specific - \c{GL_NV_path_rendering} methods are available. The choice is made at - runtime, depending on the graphics driver's capabilities. When this is not - desired, applications can force using the generic method by setting the - PathItem.enableVendorExtensions property to \c false. - - \li The \c software backend is fully supported. The path is rendered via - QPainter::strokePath() and QPainter::fillPath() in this case. - - \li The Direct 3D 12 backend is not currently supported. - - \li The OpenVG backend is not currently supported. - - \endlist - - \sa Path, PathMove, PathLine, PathQuad, PathCubic, PathArc, PathSvg -*/ - -QQuickPathItemPrivate::QQuickPathItemPrivate() - : componentComplete(true), - vpChanged(false), - rendererType(QQuickPathItem::UnknownRenderer), - async(false), - status(QQuickPathItem::Null), - renderer(nullptr), - enableVendorExts(true) -{ -} - -QQuickPathItemPrivate::~QQuickPathItemPrivate() -{ - delete renderer; -} - -void QQuickPathItemPrivate::_q_visualPathChanged() -{ - Q_Q(QQuickPathItem); - vpChanged = true; - q->polish(); -} - -void QQuickPathItemPrivate::setStatus(QQuickPathItem::Status newStatus) -{ - Q_Q(QQuickPathItem); - if (status != newStatus) { - status = newStatus; - emit q->statusChanged(); - } -} - -QQuickPathItem::QQuickPathItem(QQuickItem *parent) - : QQuickItem(*(new QQuickPathItemPrivate), parent) -{ - setFlag(ItemHasContents); -} - -QQuickPathItem::~QQuickPathItem() -{ -} - -/*! - \qmlproperty enumeration QtQuick::PathItem::rendererType - - This property determines which path rendering backend is active. - - \list - - \li PathItem.UnknownRenderer - The renderer is unknown. - - \li PathItem.GeometryRenderer - The generic, driver independent solution - for OpenGL. Uses the same CPU-based triangulation approach as QPainter's - OpenGL 2 paint engine. This is the default on non-NVIDIA hardware when the - default, OpenGL Qt Quick scenegraph backend is in use. - - \li PathItem.NvprRenderer - Path items are rendered by performing OpenGL - calls using the \c{GL_NV_path_rendering} extension. This is the default on - NVIDIA hardware when the default, OpenGL Qt Quick scenegraph backend is in - use. - - \li PathItem.SoftwareRenderer - Pure QPainter drawing using the raster - paint engine. This is the default, and only, option when the Qt Quick - scenegraph is running with the \c software backend. - - \endlist -*/ - -QQuickPathItem::RendererType QQuickPathItem::rendererType() const -{ - Q_D(const QQuickPathItem); - return d->rendererType; -} - -/*! - \qmlproperty bool QtQuick::PathItem::asynchronous - - When PathItem.rendererType is PathItem.GeometryRenderer, the input path is - triangulated on the CPU during the polishing phase of the PathItem. This is - potentially expensive. To offload this work to separate worker threads, set - this property to \c true. - - When enabled, making a PathItem visible will not wait for the content to - become available. Instead, the gui/main thread is not blocked and the - results of the path rendering are shown only when all the asynchronous work - has been finished. - - The default value is \c false. - */ - -bool QQuickPathItem::asynchronous() const -{ - Q_D(const QQuickPathItem); - return d->async; -} - -void QQuickPathItem::setAsynchronous(bool async) -{ - Q_D(QQuickPathItem); - if (d->async != async) { - d->async = async; - emit asynchronousChanged(); - if (d->componentComplete) - d->_q_visualPathChanged(); - } -} - -/*! - \qmlproperty bool QtQuick::PathItem::enableVendorExtensions - - This property controls the usage of non-standard OpenGL extensions like - GL_NV_path_rendering. To disable PathItem.NvprRenderer and force a uniform - behavior regardless of the graphics card and drivers, set this property to - \c false. - - The default value is \c true. - */ - -bool QQuickPathItem::enableVendorExtensions() const -{ - Q_D(const QQuickPathItem); - return d->enableVendorExts; -} - -void QQuickPathItem::setEnableVendorExtensions(bool enable) -{ - Q_D(QQuickPathItem); - if (d->enableVendorExts != enable) { - d->enableVendorExts = enable; - emit enableVendorExtensionsChanged(); - } -} - -/*! - \qmlproperty enumeration QtQuick::PathItem::status - - This property determines the status of the PathItem and is relevant when - PathItem.asynchronous is set to \c true. - - \list - - \li PathItem.Null - Not yet initialized. - - \li PathItem.Ready - The PathItem has finished processing. - - \li PathItem.Processing - The path is being processed. - - \endlist - */ - -QQuickPathItem::Status QQuickPathItem::status() const -{ - Q_D(const QQuickPathItem); - return d->status; -} - -static QQuickVisualPath *vpe_at(QQmlListProperty *property, int index) -{ - QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast(property->object)); - return d->qmlData.vp.at(index); -} - -static void vpe_append(QQmlListProperty *property, QQuickVisualPath *obj) -{ - QQuickPathItem *item = static_cast(property->object); - QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(item); - d->qmlData.vp.append(obj); - - if (d->componentComplete) { - QObject::connect(obj, SIGNAL(changed()), item, SLOT(_q_visualPathChanged())); - d->_q_visualPathChanged(); - } -} - -static int vpe_count(QQmlListProperty *property) -{ - QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast(property->object)); - return d->qmlData.vp.count(); -} - -static void vpe_clear(QQmlListProperty *property) -{ - QQuickPathItem *item = static_cast(property->object); - QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(item); - - for (QQuickVisualPath *p : d->qmlData.vp) - QObject::disconnect(p, SIGNAL(changed()), item, SLOT(_q_visualPathChanged())); - - d->qmlData.vp.clear(); - - if (d->componentComplete) - d->_q_visualPathChanged(); -} - -/*! - \qmlproperty list QtQuick::PathItem::elements - - This property holds the VisualPath objects that define the contents of the - PathItem. - - \default - */ - -QQmlListProperty QQuickPathItem::elements() -{ - return QQmlListProperty(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->qmlData.vp) - connect(p, SIGNAL(changed()), this, SLOT(_q_visualPathChanged())); - - d->_q_visualPathChanged(); -} - -void QQuickPathItem::updatePolish() -{ - Q_D(QQuickPathItem); - - if (!d->vpChanged) - return; - - d->vpChanged = false; - - if (!d->renderer) { - d->createRenderer(); - if (!d->renderer) - return; - emit rendererChanged(); - } - - // endSync() is where expensive calculations may happen (or get kicked off - // on worker threads), depending on the backend. Therefore do this only - // when the item is visible. - if (isVisible()) - d->sync(); - - update(); -} - -void QQuickPathItem::itemChange(ItemChange change, const ItemChangeData &data) -{ - Q_D(QQuickPathItem); - - // sync may have been deferred; do it now if the item became visible - if (change == ItemVisibleHasChanged && data.boolValue) - d->_q_visualPathChanged(); - - QQuickItem::itemChange(change, data); -} - -QSGNode *QQuickPathItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *) -{ - // Called on the render thread, with the gui thread blocked. We can now - // safely access gui thread data. - - Q_D(QQuickPathItem); - if (d->renderer) { - if (!node) - node = d->createNode(); - d->renderer->updateNode(); - } - return node; -} - -// the renderer object lives on the gui thread -void QQuickPathItemPrivate::createRenderer() -{ - Q_Q(QQuickPathItem); - QSGRendererInterface *ri = q->window()->rendererInterface(); - if (!ri) - return; - - switch (ri->graphicsApi()) { -#ifndef QT_NO_OPENGL - case QSGRendererInterface::OpenGL: - if (enableVendorExts && QQuickPathItemNvprRenderNode::isSupported()) { - rendererType = QQuickPathItem::NvprRenderer; - renderer = new QQuickPathItemNvprRenderer; - } else { - rendererType = QQuickPathItem::GeometryRenderer; - renderer = new QQuickPathItemGenericRenderer(q); - } - break; -#endif - case QSGRendererInterface::Software: - rendererType = QQuickPathItem::SoftwareRenderer; - renderer = new QQuickPathItemSoftwareRenderer; - break; - default: - qWarning("No path backend for this graphics API yet"); - break; - } -} - -// the node lives on the render thread -QSGNode *QQuickPathItemPrivate::createNode() -{ - Q_Q(QQuickPathItem); - QSGNode *node = nullptr; - if (!q->window()) - return node; - QSGRendererInterface *ri = q->window()->rendererInterface(); - if (!ri) - return node; - - switch (ri->graphicsApi()) { -#ifndef QT_NO_OPENGL - case QSGRendererInterface::OpenGL: - if (enableVendorExts && QQuickPathItemNvprRenderNode::isSupported()) { - node = new QQuickPathItemNvprRenderNode; - static_cast(renderer)->setNode( - static_cast(node)); - } else { - node = new QQuickPathItemGenericNode; - static_cast(renderer)->setRootNode( - static_cast(node)); - } - break; -#endif - case QSGRendererInterface::Software: - node = new QQuickPathItemSoftwareRenderNode(q); - static_cast(renderer)->setNode( - static_cast(node)); - break; - default: - qWarning("No path backend for this graphics API yet"); - break; - } - - return node; -} - -static void q_asyncPathItemReady(void *data) -{ - QQuickPathItemPrivate *self = static_cast(data); - self->setStatus(QQuickPathItem::Ready); -} - -void QQuickPathItemPrivate::sync() -{ - const bool useAsync = async && renderer->flags().testFlag(QQuickAbstractPathRenderer::SupportsAsync); - if (useAsync) { - setStatus(QQuickPathItem::Processing); - renderer->setAsyncCallback(q_asyncPathItemReady, this); - } - - if (!jsData.isValid()) { - // Standard route: The path and stroke/fill parameters are provided via - // VisualPath and Path. - const int count = qmlData.vp.count(); - renderer->beginSync(count); - - for (int i = 0; i < count; ++i) { - QQuickVisualPath *p = qmlData.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; - } - - renderer->endSync(useAsync); - } else { - // Path and stroke/fill params provided from JavaScript. This avoids - // QObjects at the expense of not supporting changes afterwards. - const int count = jsData.paths.count(); - renderer->beginSync(count); - - for (int i = 0; i < count; ++i) { - renderer->setJSPath(i, jsData.paths[i]); - const QQuickPathItemStrokeFillParams sfp(jsData.sfp[i]); - renderer->setStrokeColor(i, sfp.strokeColor); - renderer->setStrokeWidth(i, sfp.strokeWidth); - renderer->setFillColor(i, sfp.fillColor); - renderer->setFillRule(i, sfp.fillRule); - renderer->setJoinStyle(i, sfp.joinStyle, sfp.miterLimit); - renderer->setCapStyle(i, sfp.capStyle); - renderer->setStrokeStyle(i, sfp.strokeStyle, sfp.dashOffset, sfp.dashPattern); - renderer->setFillGradient(i, sfp.fillGradient); - } - - renderer->endSync(useAsync); - } - - if (!useAsync) - setStatus(QQuickPathItem::Ready); -} - -// ***** gradient support ***** - -/*! - \qmltype PathGradientStop - \instantiates QQuickPathGradientStop - \inqmlmodule QtQuick - \ingroup qtquick-paths - \ingroup qtquick-views - \inherits Object - \brief Defines a color at a position in a gradient - \since 5.10 - */ - -QQuickPathGradientStop::QQuickPathGradientStop(QObject *parent) - : QObject(parent), - m_position(0), - m_color(Qt::black) -{ -} - -/*! - \qmlproperty real QtQuick::PathGradientStop::position - - The position and color properties describe the color used at a given - position in a gradient, as represented by a gradient stop. - - The default value is 0. - */ - -qreal QQuickPathGradientStop::position() const -{ - return m_position; -} - -void QQuickPathGradientStop::setPosition(qreal position) -{ - if (m_position != position) { - m_position = position; - if (QQuickPathGradient *grad = qobject_cast(parent())) - emit grad->updated(); - } -} - -/*! - \qmlproperty real QtQuick::PathGradientStop::color - - The position and color properties describe the color used at a given - position in a gradient, as represented by a gradient stop. - - The default value is \c black. - */ - -QColor QQuickPathGradientStop::color() const -{ - return m_color; -} - -void QQuickPathGradientStop::setColor(const QColor &color) -{ - if (m_color != color) { - m_color = color; - if (QQuickPathGradient *grad = qobject_cast(parent())) - emit grad->updated(); - } -} - -/*! - \qmltype PathGradient - \instantiates QQuickPathGradient - \inqmlmodule QtQuick - \ingroup qtquick-paths - \ingroup qtquick-views - \inherits Object - \brief Base type of PathItem fill gradients - \since 5.10 - - This is an abstract base class for gradients like PathLinearGradient and - cannot be created directly. - */ - -QQuickPathGradient::QQuickPathGradient(QObject *parent) - : QObject(parent), - m_spread(PadSpread) -{ -} - -int QQuickPathGradient::countStops(QQmlListProperty *list) -{ - QQuickPathGradient *grad = qobject_cast(list->object); - Q_ASSERT(grad); - return grad->m_stops.count(); -} - -QObject *QQuickPathGradient::atStop(QQmlListProperty *list, int index) -{ - QQuickPathGradient *grad = qobject_cast(list->object); - Q_ASSERT(grad); - return grad->m_stops.at(index); -} - -void QQuickPathGradient::appendStop(QQmlListProperty *list, QObject *stop) -{ - QQuickPathGradientStop *sstop = qobject_cast(stop); - if (!sstop) { - qWarning("Gradient stop list only supports QQuickPathGradientStop elements"); - return; - } - QQuickPathGradient *grad = qobject_cast(list->object); - Q_ASSERT(grad); - sstop->setParent(grad); - grad->m_stops.append(sstop); -} - -/*! - \qmlproperty list QtQuick::PathGradient::stops - \default - - The list of PathGradientStop objects defining the colors at given positions - in the gradient. - */ - -QQmlListProperty QQuickPathGradient::stops() -{ - return QQmlListProperty(this, nullptr, - &QQuickPathGradient::appendStop, - &QQuickPathGradient::countStops, - &QQuickPathGradient::atStop, - nullptr); -} - -QGradientStops QQuickPathGradient::sortedGradientStops() const -{ - QGradientStops result; - for (int i = 0; i < m_stops.count(); ++i) { - QQuickPathGradientStop *s = static_cast(m_stops[i]); - int j = 0; - while (j < result.count() && result[j].first < s->position()) - ++j; - result.insert(j, QGradientStop(s->position(), s->color())); - } - return result; -} - -/*! - \qmlproperty enumeration QtQuick::PathGradient::spred - - Specifies how the area outside the gradient area should be filled. The - default value is PathGradient.PadSpread. - - \list - \li PathGradient.PadSpread - The area is filled with the closest stop color. - \li PathGradient.RepeatSpread - The gradient is repeated outside the gradient area. - \li PathGradient.ReflectSpread - The gradient is reflected outside the gradient area. - \endlist - */ - -QQuickPathGradient::SpreadMode QQuickPathGradient::spread() const -{ - return m_spread; -} - -void QQuickPathGradient::setSpread(SpreadMode mode) -{ - if (m_spread != mode) { - m_spread = mode; - emit spreadChanged(); - emit updated(); - } -} - -/*! - \qmltype PathLinearGradient - \instantiates QQuickPathLinearGradient - \inqmlmodule QtQuick - \ingroup qtquick-paths - \ingroup qtquick-views - \inherits PathGradient - \brief Linear gradient - \since 5.10 - - Linear gradients interpolate colors between start and end points. Outside - these points the gradient is either padded, reflected or repeated depending - on the spread type. - - \sa QLinearGradient - */ - -QQuickPathLinearGradient::QQuickPathLinearGradient(QObject *parent) - : QQuickPathGradient(parent) -{ -} - -/*! - \qmlproperty real QtQuick::PathLinearGradient::x1 - \qmlproperty real QtQuick::PathLinearGradient::y1 - \qmlproperty real QtQuick::PathLinearGradient::x2 - \qmlproperty real QtQuick::PathLinearGradient::y2 - - These properties define the start and end points between which color - interpolation occurs. By default both the stard and end points are set to - (0, 0). - */ - -qreal QQuickPathLinearGradient::x1() const -{ - return m_start.x(); -} - -void QQuickPathLinearGradient::setX1(qreal v) -{ - if (m_start.x() != v) { - m_start.setX(v); - emit x1Changed(); - emit updated(); - } -} - -qreal QQuickPathLinearGradient::y1() const -{ - return m_start.y(); -} - -void QQuickPathLinearGradient::setY1(qreal v) -{ - if (m_start.y() != v) { - m_start.setY(v); - emit y1Changed(); - emit updated(); - } -} - -qreal QQuickPathLinearGradient::x2() const -{ - return m_end.x(); -} - -void QQuickPathLinearGradient::setX2(qreal v) -{ - if (m_end.x() != v) { - m_end.setX(v); - emit x2Changed(); - emit updated(); - } -} - -qreal QQuickPathLinearGradient::y2() const -{ - return m_end.y(); -} - -void QQuickPathLinearGradient::setY2(qreal v) -{ - if (m_end.y() != v) { - m_end.setY(v); - emit y2Changed(); - emit updated(); - } -} - -#ifndef QT_NO_OPENGL - -// contexts sharing with each other get the same cache instance -class QQuickPathItemGradientCacheWrapper -{ -public: - QQuickPathItemGradientCache *get(QOpenGLContext *context) - { - return m_resource.value(context); - } - -private: - QOpenGLMultiGroupSharedResource m_resource; -}; - -QQuickPathItemGradientCache *QQuickPathItemGradientCache::currentCache() -{ - static QQuickPathItemGradientCacheWrapper qt_path_gradient_caches; - return qt_path_gradient_caches.get(QOpenGLContext::currentContext()); -} - -// let QOpenGLContext manage the lifetime of the cached textures -QQuickPathItemGradientCache::~QQuickPathItemGradientCache() -{ - m_cache.clear(); -} - -void QQuickPathItemGradientCache::invalidateResource() -{ - m_cache.clear(); -} - -void QQuickPathItemGradientCache::freeResource(QOpenGLContext *) -{ - qDeleteAll(m_cache); - m_cache.clear(); -} - -static void generateGradientColorTable(const QQuickPathItemGradientCache::GradientDesc &gradient, - uint *colorTable, int size, float opacity) -{ - int pos = 0; - const QGradientStops &s = gradient.stops; - const bool colorInterpolation = true; - - uint alpha = qRound(opacity * 256); - uint current_color = ARGB_COMBINE_ALPHA(s[0].second.rgba(), alpha); - qreal incr = 1.0 / qreal(size); - qreal fpos = 1.5 * incr; - colorTable[pos++] = ARGB2RGBA(qPremultiply(current_color)); - - while (fpos <= s.first().first) { - colorTable[pos] = colorTable[pos - 1]; - pos++; - fpos += incr; - } - - if (colorInterpolation) - current_color = qPremultiply(current_color); - - const int sLast = s.size() - 1; - for (int i = 0; i < sLast; ++i) { - qreal delta = 1/(s[i+1].first - s[i].first); - uint next_color = ARGB_COMBINE_ALPHA(s[i + 1].second.rgba(), alpha); - if (colorInterpolation) - next_color = qPremultiply(next_color); - - while (fpos < s[i+1].first && pos < size) { - int dist = int(256 * ((fpos - s[i].first) * delta)); - int idist = 256 - dist; - if (colorInterpolation) - colorTable[pos] = ARGB2RGBA(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist)); - else - colorTable[pos] = ARGB2RGBA(qPremultiply(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist))); - ++pos; - fpos += incr; - } - current_color = next_color; - } - - Q_ASSERT(s.size() > 0); - - uint last_color = ARGB2RGBA(qPremultiply(ARGB_COMBINE_ALPHA(s[sLast].second.rgba(), alpha))); - for ( ; pos < size; ++pos) - colorTable[pos] = last_color; - - colorTable[size-1] = last_color; -} - -QSGTexture *QQuickPathItemGradientCache::get(const GradientDesc &grad) -{ - QSGPlainTexture *tx = m_cache[grad]; - if (!tx) { - QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); - GLuint id; - f->glGenTextures(1, &id); - f->glBindTexture(GL_TEXTURE_2D, id); - static const uint W = 1024; // texture size is 1024x1 - uint buf[W]; - generateGradientColorTable(grad, buf, W, 1.0f); - f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, W, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf); - tx = new QSGPlainTexture; - tx->setTextureId(id); - switch (grad.spread) { - case QQuickPathGradient::PadSpread: - tx->setHorizontalWrapMode(QSGTexture::ClampToEdge); - tx->setVerticalWrapMode(QSGTexture::ClampToEdge); - break; - case QQuickPathGradient::RepeatSpread: - tx->setHorizontalWrapMode(QSGTexture::Repeat); - tx->setVerticalWrapMode(QSGTexture::Repeat); - break; - case QQuickPathGradient::ReflectSpread: - tx->setHorizontalWrapMode(QSGTexture::MirroredRepeat); - tx->setVerticalWrapMode(QSGTexture::MirroredRepeat); - break; - default: - qWarning("Unknown gradient spread mode %d", grad.spread); - break; - } - m_cache[grad] = tx; - } - return tx; -} - -#endif // QT_NO_OPENGL - -// ***** JS-based alternative for creating static paths, (mostly) without QObjects ***** - -class QQuickPathItemJSEngineData : public QV8Engine::Deletable -{ -public: - QQuickPathItemJSEngineData(QV4::ExecutionEngine *engine); - - QV4::PersistentValue pathProto; - QV4::PersistentValue strokeFillParamsProto; -}; - -V4_DEFINE_EXTENSION(QQuickPathItemJSEngineData, engineData) - -namespace QV4 { -namespace Heap { - -struct QQuickPathItemJSPathPrototype : Object { - void init() { Object::init(); } -}; - -struct QQuickPathItemJSPath : Object { - void init() { Object::init(); } - QQuickPathItemPathObject *obj; -}; - -struct QQuickPathItemJSStrokeFillParamsPrototype : Object { - void init() { Object::init(); } -}; - -struct QQuickPathItemJSStrokeFillParams : Object { - void init() { Object::init(); } - QQuickPathItemStrokeFillParamsObject *obj; -}; - -} // namespace Heap -} // namespace QV4 - -struct QQuickPathItemJSPathPrototype : public QV4::Object -{ - V4_OBJECT2(QQuickPathItemJSPathPrototype, QV4::Object) -public: - static QV4::Heap::QQuickPathItemJSPathPrototype *create(QV4::ExecutionEngine *engine) - { - QV4::Scope scope(engine); - auto obj = engine->memoryManager->allocObject(); - QV4::Scoped o(scope, obj); - - o->defineDefaultProperty(QStringLiteral("clear"), method_clear, 0); - o->defineDefaultProperty(QStringLiteral("moveTo"), method_moveTo, 0); - o->defineDefaultProperty(QStringLiteral("lineTo"), method_lineTo, 0); - o->defineDefaultProperty(QStringLiteral("quadTo"), method_quadTo, 0); - o->defineDefaultProperty(QStringLiteral("cubicTo"), method_cubicTo, 0); - o->defineDefaultProperty(QStringLiteral("arcTo"), method_arcTo, 0); - - return o->d(); - } - - static void method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_moveTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_lineTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_quadTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_cubicTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_arcTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); -}; - -DEFINE_OBJECT_VTABLE(QQuickPathItemJSPathPrototype); - -struct QQuickPathItemJSStrokeFillParamsPrototype : public QV4::Object -{ - V4_OBJECT2(QQuickPathItemJSStrokeFillParamsPrototype, QV4::Object) -public: - static QV4::Heap::QQuickPathItemJSStrokeFillParamsPrototype *create(QV4::ExecutionEngine *engine) - { - QV4::Scope scope(engine); - auto obj = engine->memoryManager->allocObject(); - QV4::Scoped o(scope, obj); - - o->defineDefaultProperty(QStringLiteral("clear"), method_clear, 0); - - return o->d(); - } - - static void method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); -}; - -DEFINE_OBJECT_VTABLE(QQuickPathItemJSStrokeFillParamsPrototype); - -struct QQuickPathItemJSPath : public QV4::Object -{ - V4_OBJECT2(QQuickPathItemJSPath, QV4::Object) -}; - -DEFINE_OBJECT_VTABLE(QQuickPathItemJSPath); - -struct QQuickPathItemJSStrokeFillParams : public QV4::Object -{ - V4_OBJECT2(QQuickPathItemJSStrokeFillParams, QV4::Object) - - static void method_get_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); -}; - -DEFINE_OBJECT_VTABLE(QQuickPathItemJSStrokeFillParams); - -void QQuickPathItemJSPathPrototype::method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - r->d()->obj->clear(); - - scope.result = callData->thisObject.asReturnedValue(); -} - -void QQuickPathItemJSPathPrototype::method_moveTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - if (callData->argc >= 2) { - QQuickPathItemPathObject *p = r->d()->obj; - p->path.cmd.append(QQuickPathItemPath::MoveTo); - p->path.coords.append(callData->args[0].toNumber()); - p->path.coords.append(callData->args[1].toNumber()); - } - - scope.result = callData->thisObject.asReturnedValue(); -} - -void QQuickPathItemJSPathPrototype::method_lineTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - if (callData->argc >= 2) { - QQuickPathItemPathObject *p = r->d()->obj; - p->path.cmd.append(QQuickPathItemPath::LineTo); - p->path.coords.append(callData->args[0].toNumber()); - p->path.coords.append(callData->args[1].toNumber()); - } - - scope.result = callData->thisObject.asReturnedValue(); -} - -void QQuickPathItemJSPathPrototype::method_quadTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - if (callData->argc >= 4) { - QQuickPathItemPathObject *p = r->d()->obj; - p->path.cmd.append(QQuickPathItemPath::QuadTo); - const QV4::Value *v = callData->args; - p->path.coords.append(v[0].toNumber()); // cx - p->path.coords.append(v[1].toNumber()); // cy - p->path.coords.append(v[2].toNumber()); // x - p->path.coords.append(v[3].toNumber()); // y - } - - scope.result = callData->thisObject.asReturnedValue(); -} - -void QQuickPathItemJSPathPrototype::method_cubicTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - if (callData->argc >= 6) { - QQuickPathItemPathObject *p = r->d()->obj; - p->path.cmd.append(QQuickPathItemPath::CubicTo); - const QV4::Value *v = callData->args; - p->path.coords.append(v[0].toNumber()); // c1x - p->path.coords.append(v[1].toNumber()); // c1y - p->path.coords.append(v[2].toNumber()); // c2x - p->path.coords.append(v[3].toNumber()); // c2y - p->path.coords.append(v[4].toNumber()); // x - p->path.coords.append(v[5].toNumber()); // y - } - - scope.result = callData->thisObject.asReturnedValue(); -} - -void QQuickPathItemJSPathPrototype::method_arcTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - if (callData->argc >= 7) { - QQuickPathItemPathObject *p = r->d()->obj; - p->path.cmd.append(QQuickPathItemPath::ArcTo); - const QV4::Value *v = callData->args; - p->path.coords.append(v[0].toNumber()); // radiusX - p->path.coords.append(v[1].toNumber()); // radiusY - p->path.coords.append(v[2].toNumber()); // xAxisRotation - p->path.coords.append(v[3].toNumber()); // x - p->path.coords.append(v[4].toNumber()); // y - p->path.coords.append(v[5].toNumber()); // sweepFlag - p->path.coords.append(v[6].toNumber()); // largeArc - } - - scope.result = callData->thisObject.asReturnedValue(); -} - -void QQuickPathItemJSStrokeFillParamsPrototype::method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - r->d()->obj->clear(); - - scope.result = callData->thisObject.asReturnedValue(); -} - -Q_QUICK_PRIVATE_EXPORT QColor qt_color_from_string(const QV4::Value &name); // qquickcontext2d.cpp - -static inline QString qt_color_string(const QColor &color) -{ - if (color.alpha() == 255) - return color.name(); - QString alphaString = QString::number(color.alphaF(), 'f'); - while (alphaString.endsWith(QLatin1Char('0'))) - alphaString.chop(1); - if (alphaString.endsWith(QLatin1Char('.'))) - alphaString += QLatin1Char('0'); - return QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString); -} - -void QQuickPathItemJSStrokeFillParams::method_get_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = QV4::Encode(scope.engine->newString(qt_color_string(r->d()->obj->sfp.strokeColor))); -} - -void QQuickPathItemJSStrokeFillParams::method_set_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isString()) - r->d()->obj->sfp.strokeColor = qt_color_from_string(value); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.strokeWidth); -} - -void QQuickPathItemJSStrokeFillParams::method_set_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - r->d()->obj->sfp.strokeWidth = value->toNumber(); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = QV4::Encode(scope.engine->newString(qt_color_string(r->d()->obj->sfp.fillColor))); -} - -void QQuickPathItemJSStrokeFillParams::method_set_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isString()) - r->d()->obj->sfp.fillColor = qt_color_from_string(value); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.fillRule); -} - -void QQuickPathItemJSStrokeFillParams::method_set_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isInt32()) - r->d()->obj->sfp.fillRule = QQuickVisualPath::FillRule(value->integerValue()); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.joinStyle); -} - -void QQuickPathItemJSStrokeFillParams::method_set_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isInt32()) - r->d()->obj->sfp.joinStyle = QQuickVisualPath::JoinStyle(value->integerValue()); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.miterLimit); -} - -void QQuickPathItemJSStrokeFillParams::method_set_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - r->d()->obj->sfp.miterLimit = value->toNumber(); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.capStyle); -} - -void QQuickPathItemJSStrokeFillParams::method_set_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isInt32()) - r->d()->obj->sfp.capStyle = QQuickVisualPath::CapStyle(value->integerValue()); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.strokeStyle); -} - -void QQuickPathItemJSStrokeFillParams::method_set_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isInt32()) - r->d()->obj->sfp.strokeStyle = QQuickVisualPath::StrokeStyle(value->integerValue()); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.dashOffset); -} - -void QQuickPathItemJSStrokeFillParams::method_set_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - r->d()->obj->sfp.dashOffset = value->toNumber(); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedArrayObject a(scope, scope.engine->newArrayObject()); - QQuickPathItemStrokeFillParamsObject *p = r->d()->obj; - a->arrayReserve(p->sfp.dashPattern.count()); - QV4::ScopedValue v(scope); - for (int i = 0; i < p->sfp.dashPattern.count(); ++i) - a->arrayPut(i, (v = scope.engine->fromVariant(p->sfp.dashPattern[i]))); - a->setArrayLengthUnchecked(p->sfp.dashPattern.count()); - - scope.result = a.asReturnedValue(); -} - -void QQuickPathItemJSStrokeFillParams::method_set_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isObject()) { - QV4::Scoped ao(scope, value); - if (!!ao) { - QQuickPathItemStrokeFillParamsObject *p = r->d()->obj; - p->sfp.dashPattern.resize(ao->getLength()); - QV4::ScopedValue val(scope); - for (int i = 0; i < p->sfp.dashPattern.count(); ++i) { - val = ao->getIndexed(i); - p->sfp.dashPattern[i] = val->toNumber(); - } - } - } - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = r->d()->obj->v4fillGradient.value(); -} - -void QQuickPathItemJSStrokeFillParams::method_set_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - QV4::Scoped qobjectWrapper(scope, value); - if (!!qobjectWrapper) { - if (QQuickPathGradient *grad = qobject_cast(qobjectWrapper->object())) { - r->d()->obj->v4fillGradient.set(scope.engine, value); - r->d()->obj->sfp.fillGradient = grad; - } - } else { - r->d()->obj->v4fillGradient.set(scope.engine, nullptr); - r->d()->obj->sfp.fillGradient = nullptr; - } - - scope.result = QV4::Encode::undefined(); -} - -QQuickPathItemJSEngineData::QQuickPathItemJSEngineData(QV4::ExecutionEngine *v4) -{ - QV4::Scope scope(v4); - - QV4::ScopedObject proto(scope, QQuickPathItemJSPathPrototype::create(v4)); - pathProto = proto; - - proto = QV4::ScopedObject(scope, QQuickPathItemJSStrokeFillParamsPrototype::create(v4)); - - proto->defineAccessorProperty(QStringLiteral("strokeColor"), - QQuickPathItemJSStrokeFillParams::method_get_strokeColor, - QQuickPathItemJSStrokeFillParams::method_set_strokeColor); - proto->defineAccessorProperty(QStringLiteral("strokeWidth"), - QQuickPathItemJSStrokeFillParams::method_get_strokeWidth, - QQuickPathItemJSStrokeFillParams::method_set_strokeWidth); - proto->defineAccessorProperty(QStringLiteral("fillColor"), - QQuickPathItemJSStrokeFillParams::method_get_fillColor, - QQuickPathItemJSStrokeFillParams::method_set_fillColor); - proto->defineAccessorProperty(QStringLiteral("fillRule"), - QQuickPathItemJSStrokeFillParams::method_get_fillRule, - QQuickPathItemJSStrokeFillParams::method_set_fillRule); - proto->defineAccessorProperty(QStringLiteral("joinStyle"), - QQuickPathItemJSStrokeFillParams::method_get_joinStyle, - QQuickPathItemJSStrokeFillParams::method_set_joinStyle); - proto->defineAccessorProperty(QStringLiteral("miterLimit"), - QQuickPathItemJSStrokeFillParams::method_get_miterLimit, - QQuickPathItemJSStrokeFillParams::method_set_miterLimit); - proto->defineAccessorProperty(QStringLiteral("capStyle"), - QQuickPathItemJSStrokeFillParams::method_get_capStyle, - QQuickPathItemJSStrokeFillParams::method_set_capStyle); - proto->defineAccessorProperty(QStringLiteral("strokeStyle"), - QQuickPathItemJSStrokeFillParams::method_get_strokeStyle, - QQuickPathItemJSStrokeFillParams::method_set_strokeStyle); - proto->defineAccessorProperty(QStringLiteral("dashOffset"), - QQuickPathItemJSStrokeFillParams::method_get_dashOffset, - QQuickPathItemJSStrokeFillParams::method_set_dashOffset); - proto->defineAccessorProperty(QStringLiteral("dashPattern"), - QQuickPathItemJSStrokeFillParams::method_get_dashPattern, - QQuickPathItemJSStrokeFillParams::method_set_dashPattern); - proto->defineAccessorProperty(QStringLiteral("fillGradient"), - QQuickPathItemJSStrokeFillParams::method_get_fillGradient, - QQuickPathItemJSStrokeFillParams::method_set_fillGradient); - - strokeFillParamsProto = proto; -} - -void QQuickPathItemPathObject::setV4Engine(QV4::ExecutionEngine *engine) -{ - QQuickPathItemJSEngineData *ed = engineData(engine); - QV4::Scope scope(engine); - QV4::Scoped wrapper(scope, engine->memoryManager->allocObject()); - QV4::ScopedObject p(scope, ed->pathProto.value()); - wrapper->setPrototype(p); - wrapper->d()->obj = this; - m_v4value = wrapper; -} - -/*! - \qmltype JSPath - \inqmlmodule QtQuick - \ingroup qtquick-path - \brief Describes a path via a JavaScript API - */ - -/*! - \qmlmethod void QtQuick::JSPath::moveTo(x, y) - - Moves the path's position to the absolute position specified by (\a x, \a y). - */ - -/*! - \qmlmethod void QtQuick::JSPath::lineTo(x, y) - - Defines a straight line to the absolute position specified by (\a x, \a y). - */ - -/*! - \qmlmethod void QtQuick::JSPath::quadTo(cx, cy, x, y) - - Defines a quadratic Bezier curve with a control point (\a cx, \a cy) and an - end point of (\a x, \a y). - */ - -/*! - \qmlmethod void QtQuick::JSPath::cubicTo(c1x, c1y, c2x, c2y, x, y) - - Defines a cubic Bezier curve with two control points (\a c1x, \a c1y) and - (\a c2x, \a c2y), and an end point of (\a x, \a y). - */ - -/*! - \qmlmethod void QtQuick::JSPath::arcTo(radiusX, radiusY, xAxisRotation, x, y, sweepFlag, largeArc) - - Defines an elliptical arc, following the elliptical arc command in SVG. See - \l{https://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands}{the - SVG path specification} for details on the parameters. - */ - -/*! - \qmlmethod void QtQuick::JSPath::clear() - - Clears the path object removing all path elements. This is more lightweight - than creating a new JSPath object. - */ - -void QQuickPathItemPathObject::clear() -{ - path = QQuickPathItemPath(); -} - -/*! - \qmltype StrokeFillParams - \inqmlmodule QtQuick - \ingroup qtquick-path - \brief Describes stroke and fill parameters via a JavaScript API - - The properties of StrokeFillParams objects correspond 1:1 to VisualPath - properties. The possible values for enumerations are the same as well, for - example: - - \code - sfp.strokeStyle = VisualPath.DashLine; - sfp.capStyle = VisualPath.RoundCap; - \endcode - */ - -/*! - \qmlproperty color QtQuick::StrokeFillParams::strokeColor - */ - -/*! - \qmlproperty real QtQuick::StrokeFillParams::strokeWidth - */ - -/*! - \qmlproperty color QtQuick::StrokeFillParams::fillColor - */ - -/*! - \qmlproperty enumeration QtQuick::StrokeFillParams::fillRule - */ - -/*! - \qmlproperty enumeration QtQuick::StrokeFillParams::joinStyle - */ - -/*! - \qmlproperty int QtQuick::StrokeFillParams::miterLimit - */ - -/*! - \qmlproperty enumeration QtQuick::StrokeFillParams::capStyle - */ - -/*! - \qmlproperty enumeration QtQuick::StrokeFillParams::strokeStyle - */ - -/*! - \qmlproperty real QtQuick::StrokeFillParams::dashOffset - */ - -/*! - \qmlproperty list QtQuick::StrokeFillParams::dashPattern - - The dash pattern can be specified using JavaScript arrays. - - \code - sfp.dashPattern = [ 4, 2 ]; - \endcode - */ - -/*! - \qmlproperty object QtQuick::StrokeFillParams::fillGradient - - Sets the fill gradient. The default value is null. Gradients cannot be - created from JavaScript. Instead, reference a PathLinearGradient or other - item by id. - - \code - PathLinearGradient { id: grad; ... } - ... - sfp.fillGradient = grad; - \endcode - */ - -/*! - \qmlmethod void QtQuick::StrokeFillParams::clear() - - Resets all values to their defaults. This is more lightweight than creating - a new StrokeFillParams object. - */ - -void QQuickPathItemStrokeFillParamsObject::clear() -{ - sfp = QQuickPathItemStrokeFillParams(); - if (!v4fillGradient.isNullOrUndefined()) - v4fillGradient.set(v4fillGradient.engine(), nullptr); -} - -void QQuickPathItemStrokeFillParamsObject::setV4Engine(QV4::ExecutionEngine *engine) -{ - QQuickPathItemJSEngineData *ed = engineData(engine); - QV4::Scope scope(engine); - QV4::Scoped wrapper(scope, engine->memoryManager->allocObject()); - QV4::ScopedObject p(scope, ed->strokeFillParamsProto.value()); - wrapper->setPrototype(p); - wrapper->d()->obj = this; - m_v4value = wrapper; -} - -/*! - \qmlmethod JSPath QtQuick::PathItem::newPath() - - Creates and returns a new object that describes a path and offers a - JavaScript API. Paired with a stroke-fill parameter object it is - equivalent to a VisualPath. - - The following two snippets are equivalent when it comes to the end result: - - \code - var p = pathItem.newPath(); - var sfp = pathItem.newStrokeFillParams(); - sfp.fillColor = "white"; - sfp.strokeColor = "black"; - sfp.strokeWidth = 0.172; - p.moveTo(-122.304, 84.285); - p.cubicTo(-122.304, 84.285, -122.203, 86.179, -123.027, 86.16); - pathItem.appendVisualPath(p, sfp); - \endcode - - \code - PathItem { - VisualPath { - fillColor: "white" - strokeColor: "black" - strokeWidth: 0.172 - Path { - startX: -122.304; startY: 84.285 - PathCubic { control1X: -122.304; control1Y: 84.285; control2X: -122.203; control2Y: 86.179; x: -123.027; y: 86.16 } - } - } - } - \endcode - - The latter offers a full declarative API, with the possibility to binding - to and animating properties, while the former uses less resources due to - greatly reducing the number of QObject instances created. -*/ - -void QQuickPathItem::newPath(QQmlV4Function *args) -{ - QQuickPathItemPathObject *obj = new QQuickPathItemPathObject(this); - obj->setV4Engine(QQmlEnginePrivate::get(qmlEngine(this))->v4engine()); - args->setReturnValue(obj->v4value()); -} - -/*! - \qmlmethod StrokeFillParams QtQuick::PathItem::newStrokeFillParams() - - Creates and returns a new object that describes stroke and fill parameters - and offers a JavaScript API. Paired with a path object it is equivalent to - a VisualPath. - */ - -void QQuickPathItem::newStrokeFillParams(QQmlV4Function *args) -{ - QQuickPathItemStrokeFillParamsObject *obj = new QQuickPathItemStrokeFillParamsObject(this); - obj->setV4Engine(QQmlEnginePrivate::get(qmlEngine(this))->v4engine()); - args->setReturnValue(obj->v4value()); -} - -/*! - \qmlmethod void QtQuick::PathItem::clearVisualPaths() - - Clears the list of visual paths. - - \note This applies only to path and stroke-fill parameter objects registered - via appendVisualPaths(). - */ - -void QQuickPathItem::clearVisualPaths(QQmlV4Function *args) -{ - Q_UNUSED(args); - Q_D(QQuickPathItem); - d->jsData.paths.clear(); - d->jsData.sfp.clear(); -} - -/*! - \qmlmethod void QtQuick::PathItem::commitVisualPaths() - - Updates the PathItem. - - In order to avoid rendering a half-prepared PathItem, calling - PathItem.appendVisualPath() does not trigger any processing. Instead, - applications must call this function when all path and stroke-fill - parameter objects are registered. - */ - -void QQuickPathItem::commitVisualPaths(QQmlV4Function *args) -{ - Q_UNUSED(args); - Q_D(QQuickPathItem); - d->_q_visualPathChanged(); -} - -/*! - \qmlmethod void QtQuick::PathItem::appendVisualPath(object path, object strokeFillParams) - - Adds the visual path compoes of \a path and \a strokeFillParams into the - PathItem. - - \note The declarative and imprative (JavaScript) APIs of PathItem use - independent data structures. Calling this function has no effect on the - PathItem.elements property and vice versa. Once this function is called, - the PathItem will only consider the data registered via this function and - will ignore the declarative elements property. - */ - -void QQuickPathItem::appendVisualPath(QQmlV4Function *args) -{ - if (args->length() < 2) - return; - - Q_D(QQuickPathItem); - QV4::Scope scope(args->v4engine()); - QV4::Scoped jsp(scope, (*args)[0]); - QV4::Scoped jssfp(scope, (*args)[1]); - - const QQuickPathItemPath &path(jsp->d()->obj->path); - const QQuickPathItemStrokeFillParams &sfp(jssfp->d()->obj->sfp); - - d->jsData.paths.append(path); - d->jsData.sfp.append(sfp); -} - -QT_END_NAMESPACE - -#include "moc_qquickpathitem_p.cpp" diff --git a/src/imports/pathitem/qquickpathitem_p.h b/src/imports/pathitem/qquickpathitem_p.h deleted file mode 100644 index c7c56fd5d8..0000000000 --- a/src/imports/pathitem/qquickpathitem_p.h +++ /dev/null @@ -1,333 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKPATHITEM_P_H -#define QQUICKPATHITEM_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qquickitem.h" - -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QQuickVisualPathPrivate; -class QQuickPathItemPrivate; - -class QQuickPathGradientStop : public QObject -{ - Q_OBJECT - Q_PROPERTY(qreal position READ position WRITE setPosition) - Q_PROPERTY(QColor color READ color WRITE setColor) - -public: - QQuickPathGradientStop(QObject *parent = nullptr); - - qreal position() const; - void setPosition(qreal position); - - QColor color() const; - void setColor(const QColor &color); - -private: - qreal m_position; - QColor m_color; -}; - -class QQuickPathGradient : public QObject -{ - Q_OBJECT - Q_PROPERTY(QQmlListProperty stops READ stops) - Q_PROPERTY(SpreadMode spread READ spread WRITE setSpread NOTIFY spreadChanged) - Q_CLASSINFO("DefaultProperty", "stops") - -public: - enum SpreadMode { - PadSpread, - RepeatSpread, - ReflectSpread - }; - Q_ENUM(SpreadMode) - - QQuickPathGradient(QObject *parent = nullptr); - - QQmlListProperty stops(); - - QGradientStops sortedGradientStops() const; - - SpreadMode spread() const; - void setSpread(SpreadMode mode); - -signals: - void updated(); - void spreadChanged(); - -private: - static int countStops(QQmlListProperty *list); - static QObject *atStop(QQmlListProperty *list, int index); - static void appendStop(QQmlListProperty *list, QObject *stop); - - QVector m_stops; - SpreadMode m_spread; -}; - -class QQuickPathLinearGradient : public QQuickPathGradient -{ - Q_OBJECT - Q_PROPERTY(qreal x1 READ x1 WRITE setX1 NOTIFY x1Changed) - Q_PROPERTY(qreal y1 READ y1 WRITE setY1 NOTIFY y1Changed) - Q_PROPERTY(qreal x2 READ x2 WRITE setX2 NOTIFY x2Changed) - Q_PROPERTY(qreal y2 READ y2 WRITE setY2 NOTIFY y2Changed) - Q_CLASSINFO("DefaultProperty", "stops") - -public: - QQuickPathLinearGradient(QObject *parent = nullptr); - - qreal x1() const; - void setX1(qreal v); - qreal y1() const; - void setY1(qreal v); - qreal x2() const; - void setX2(qreal v); - qreal y2() const; - void setY2(qreal v); - -signals: - void x1Changed(); - void y1Changed(); - void x2Changed(); - void y2Changed(); - -private: - QPointF m_start; - QPointF m_end; -}; - -class QQuickVisualPath : public QObject -{ - Q_OBJECT - - 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) - Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged) - Q_PROPERTY(FillRule fillRule READ fillRule WRITE setFillRule NOTIFY fillRuleChanged) - Q_PROPERTY(JoinStyle joinStyle READ joinStyle WRITE setJoinStyle NOTIFY joinStyleChanged) - Q_PROPERTY(int miterLimit READ miterLimit WRITE setMiterLimit NOTIFY miterLimitChanged) - Q_PROPERTY(CapStyle capStyle READ capStyle WRITE setCapStyle NOTIFY capStyleChanged) - Q_PROPERTY(StrokeStyle strokeStyle READ strokeStyle WRITE setStrokeStyle NOTIFY strokeStyleChanged) - Q_PROPERTY(qreal dashOffset READ dashOffset WRITE setDashOffset NOTIFY dashOffsetChanged) - Q_PROPERTY(QVector dashPattern READ dashPattern WRITE setDashPattern NOTIFY dashPatternChanged) - Q_PROPERTY(QQuickPathGradient *fillGradient READ fillGradient WRITE setFillGradient RESET resetFillGradient) - -public: - enum FillRule { - OddEvenFill = Qt::OddEvenFill, - WindingFill = Qt::WindingFill - }; - Q_ENUM(FillRule) - - enum JoinStyle { - MiterJoin = Qt::MiterJoin, - BevelJoin = Qt::BevelJoin, - RoundJoin = Qt::RoundJoin - }; - Q_ENUM(JoinStyle) - - enum CapStyle { - FlatCap = Qt::FlatCap, - SquareCap = Qt::SquareCap, - RoundCap = Qt::RoundCap - }; - Q_ENUM(CapStyle) - - enum StrokeStyle { - SolidLine = Qt::SolidLine, - DashLine = Qt::DashLine - }; - Q_ENUM(StrokeStyle) - - QQuickVisualPath(QObject *parent = nullptr); - ~QQuickVisualPath(); - - QQuickPath *path() const; - void setPath(QQuickPath *path); - - QColor strokeColor() const; - void setStrokeColor(const QColor &color); - - qreal strokeWidth() const; - void setStrokeWidth(qreal w); - - QColor fillColor() const; - void setFillColor(const QColor &color); - - FillRule fillRule() const; - void setFillRule(FillRule fillRule); - - JoinStyle joinStyle() const; - void setJoinStyle(JoinStyle style); - - int miterLimit() const; - void setMiterLimit(int limit); - - CapStyle capStyle() const; - void setCapStyle(CapStyle style); - - StrokeStyle strokeStyle() const; - void setStrokeStyle(StrokeStyle style); - - qreal dashOffset() const; - void setDashOffset(qreal offset); - - QVector dashPattern() const; - void setDashPattern(const QVector &array); - - QQuickPathGradient *fillGradient() const; - void setFillGradient(QQuickPathGradient *gradient); - void resetFillGradient(); - -Q_SIGNALS: - void changed(); - void pathChanged(); - void strokeColorChanged(); - void strokeWidthChanged(); - void fillColorChanged(); - void fillRuleChanged(); - void joinStyleChanged(); - void miterLimitChanged(); - void capStyleChanged(); - void strokeStyleChanged(); - void dashOffsetChanged(); - void dashPatternChanged(); - void fillGradientChanged(); - -private: - Q_DISABLE_COPY(QQuickVisualPath) - Q_DECLARE_PRIVATE(QQuickVisualPath) - Q_PRIVATE_SLOT(d_func(), void _q_pathChanged()) - Q_PRIVATE_SLOT(d_func(), void _q_fillGradientChanged()) -}; - -class QQuickPathItem : public QQuickItem -{ - Q_OBJECT - Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged) - Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) - Q_PROPERTY(bool enableVendorExtensions READ enableVendorExtensions WRITE setEnableVendorExtensions NOTIFY enableVendorExtensionsChanged) - Q_PROPERTY(Status status READ status NOTIFY statusChanged) - Q_PROPERTY(QQmlListProperty elements READ elements) - Q_CLASSINFO("DefaultProperty", "elements") - -public: - enum RendererType { - UnknownRenderer, - GeometryRenderer, - NvprRenderer, - SoftwareRenderer - }; - Q_ENUM(RendererType) - - enum Status { - Null, - Ready, - Processing - }; - Q_ENUM(Status) - - QQuickPathItem(QQuickItem *parent = nullptr); - ~QQuickPathItem(); - - RendererType rendererType() const; - - bool asynchronous() const; - void setAsynchronous(bool async); - - bool enableVendorExtensions() const; - void setEnableVendorExtensions(bool enable); - - Status status() const; - - QQmlListProperty elements(); - - Q_INVOKABLE void newPath(QQmlV4Function *args); - Q_INVOKABLE void newStrokeFillParams(QQmlV4Function *args); - Q_INVOKABLE void clearVisualPaths(QQmlV4Function *args); - Q_INVOKABLE void commitVisualPaths(QQmlV4Function *args); - Q_INVOKABLE void appendVisualPath(QQmlV4Function *args); - -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(); - void asynchronousChanged(); - void enableVendorExtensionsChanged(); - void statusChanged(); - -private: - Q_DISABLE_COPY(QQuickPathItem) - Q_DECLARE_PRIVATE(QQuickPathItem) - Q_PRIVATE_SLOT(d_func(), void _q_visualPathChanged()) -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQuickPathItem) - -#endif // QQUICKPATHITEM_P_H diff --git a/src/imports/pathitem/qquickpathitem_p_p.h b/src/imports/pathitem/qquickpathitem_p_p.h deleted file mode 100644 index 6dde314a30..0000000000 --- a/src/imports/pathitem/qquickpathitem_p_p.h +++ /dev/null @@ -1,282 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKPATHITEM_P_P_H -#define QQUICKPATHITEM_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qquickpathitem_p.h" -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QSGPlainTexture; - -struct QQuickPathItemPath -{ - enum Command { - MoveTo, - LineTo, - QuadTo, - CubicTo, - ArcTo - }; - - QVector cmd; - QVector coords; - - QPainterPath toPainterPath() const; -}; - -struct QQuickPathItemStrokeFillParams -{ - QQuickPathItemStrokeFillParams(); - - QColor strokeColor; - qreal strokeWidth; - QColor fillColor; - QQuickVisualPath::FillRule fillRule; - QQuickVisualPath::JoinStyle joinStyle; - int miterLimit; - QQuickVisualPath::CapStyle capStyle; - QQuickVisualPath::StrokeStyle strokeStyle; - qreal dashOffset; - QVector dashPattern; - QQuickPathGradient *fillGradient; -}; - -class QQuickAbstractPathRenderer -{ -public: - enum Flag { - SupportsAsync = 0x01 - }; - Q_DECLARE_FLAGS(Flags, Flag) - - virtual ~QQuickAbstractPathRenderer() { } - - // Gui thread - virtual void beginSync(int totalCount) = 0; - virtual void endSync(bool async) = 0; - virtual void setAsyncCallback(void (*)(void *), void *) { } - virtual Flags flags() const { return 0; } - // - QML API - virtual void setPath(int index, const QQuickPath *path) = 0; - // - JS API - virtual void setJSPath(int index, const QQuickPathItemPath &path) = 0; - // - stroke/fill parameters - 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 &dashPattern) = 0; - virtual void setFillGradient(int index, QQuickPathGradient *gradient) = 0; - - // Render thread, with gui blocked - virtual void updateNode() = 0; -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickAbstractPathRenderer::Flags) - -class QQuickVisualPathPrivate : public QObjectPrivate -{ - Q_DECLARE_PUBLIC(QQuickVisualPath) - -public: - enum Dirty { - DirtyPath = 0x01, - DirtyStrokeColor = 0x02, - DirtyStrokeWidth = 0x04, - DirtyFillColor = 0x08, - DirtyFillRule = 0x10, - DirtyStyle = 0x20, - DirtyDash = 0x40, - DirtyFillGradient = 0x80, - - DirtyAll = 0xFF - }; - - QQuickVisualPathPrivate(); - - void _q_pathChanged(); - void _q_fillGradientChanged(); - - static QQuickVisualPathPrivate *get(QQuickVisualPath *p) { return p->d_func(); } - - QQuickPath *path; - int dirty; - QQuickPathItemStrokeFillParams sfp; -}; - -class QQuickPathItemPrivate : public QQuickItemPrivate -{ - Q_DECLARE_PUBLIC(QQuickPathItem) - -public: - QQuickPathItemPrivate(); - ~QQuickPathItemPrivate(); - - void createRenderer(); - QSGNode *createNode(); - void sync(); - - void _q_visualPathChanged(); - void setStatus(QQuickPathItem::Status newStatus); - - static QQuickPathItemPrivate *get(QQuickPathItem *item) { return item->d_func(); } - - bool componentComplete; - bool vpChanged; - QQuickPathItem::RendererType rendererType; - bool async; - QQuickPathItem::Status status; - QQuickAbstractPathRenderer *renderer; - - struct { - QVector vp; - } qmlData; - - struct { - bool isValid() const { Q_ASSERT(paths.count() == sfp.count()); return !paths.isEmpty(); } - QVector paths; - QVector sfp; - } jsData; - - bool enableVendorExts; -}; - -class QQuickPathItemPathObject : public QObject -{ - Q_OBJECT - -public: - QQuickPathItemPathObject(QObject *parent = nullptr) : QObject(parent) { } - - void setV4Engine(QV4::ExecutionEngine *engine); - QV4::ReturnedValue v4value() const { return m_v4value.value(); } - - QQuickPathItemPath path; - - void clear(); - -private: - QV4::PersistentValue m_v4value; -}; - -class QQuickPathItemStrokeFillParamsObject : public QObject -{ - Q_OBJECT - -public: - QQuickPathItemStrokeFillParamsObject(QObject *parent = nullptr) : QObject(parent) { } - - void setV4Engine(QV4::ExecutionEngine *engine); - QV4::ReturnedValue v4value() const { return m_v4value.value(); } - - QQuickPathItemStrokeFillParams sfp; - QV4::PersistentValue v4fillGradient; - - void clear(); - -private: - QV4::PersistentValue m_v4value; -}; - -#ifndef QT_NO_OPENGL - -class QQuickPathItemGradientCache : public QOpenGLSharedResource -{ -public: - struct GradientDesc { - QGradientStops stops; - QPointF start; - QPointF end; - QQuickPathGradient::SpreadMode spread; - bool operator==(const GradientDesc &other) const - { - return start == other.start && end == other.end && spread == other.spread - && stops == other.stops; - } - }; - - QQuickPathItemGradientCache(QOpenGLContext *context) : QOpenGLSharedResource(context->shareGroup()) { } - ~QQuickPathItemGradientCache(); - - void invalidateResource() override; - void freeResource(QOpenGLContext *) override; - - QSGTexture *get(const GradientDesc &grad); - - static QQuickPathItemGradientCache *currentCache(); - -private: - QHash m_cache; -}; - -inline uint qHash(const QQuickPathItemGradientCache::GradientDesc &v, uint seed = 0) -{ - uint h = seed; - h += v.start.x() + v.end.y() + v.spread; - for (int i = 0; i < 3 && i < v.stops.count(); ++i) - h += v.stops[i].second.rgba(); - return h; -} - -#endif // QT_NO_OPENGL - -QT_END_NAMESPACE - -#endif diff --git a/src/imports/pathitem/qquickpathitemgenericrenderer.cpp b/src/imports/pathitem/qquickpathitemgenericrenderer.cpp deleted file mode 100644 index 4e8fe55df2..0000000000 --- a/src/imports/pathitem/qquickpathitemgenericrenderer.cpp +++ /dev/null @@ -1,775 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquickpathitemgenericrenderer_p.h" -#include -#include -#include - -#ifndef QT_NO_OPENGL -#include -#include -#include -#include -#endif - -QT_BEGIN_NAMESPACE - -static const qreal TRI_SCALE = 1; - -struct ColoredVertex // must match QSGGeometry::ColoredPoint2D -{ - float x, y; - QQuickPathItemGenericRenderer::Color4ub color; - void set(float nx, float ny, QQuickPathItemGenericRenderer::Color4ub ncolor) - { - x = nx; y = ny; color = ncolor; - } -}; - -static inline QQuickPathItemGenericRenderer::Color4ub colorToColor4ub(const QColor &c) -{ - QQuickPathItemGenericRenderer::Color4ub color = { - uchar(qRound(c.redF() * c.alphaF() * 255)), - uchar(qRound(c.greenF() * c.alphaF() * 255)), - uchar(qRound(c.blueF() * c.alphaF() * 255)), - uchar(qRound(c.alphaF() * 255)) - }; - return color; -} - -QQuickPathItemGenericStrokeFillNode::QQuickPathItemGenericStrokeFillNode(QQuickWindow *window) - : m_geometry(new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0, 0)), - m_window(window), - m_material(nullptr) -{ - setGeometry(m_geometry); - activateMaterial(MatSolidColor); -#ifdef QSG_RUNTIME_DESCRIPTION - qsgnode_set_description(this, QLatin1String("stroke-fill")); -#endif -} - -QQuickPathItemGenericStrokeFillNode::~QQuickPathItemGenericStrokeFillNode() -{ - delete m_geometry; -} - -void QQuickPathItemGenericStrokeFillNode::activateMaterial(Material m) -{ - switch (m) { - case MatSolidColor: - // Use vertexcolor material. Items with different colors remain batchable - // this way, at the expense of having to provide per-vertex color values. - if (!m_solidColorMaterial) - m_solidColorMaterial.reset(QQuickPathItemGenericMaterialFactory::createVertexColor(m_window)); - m_material = m_solidColorMaterial.data(); - break; - case MatLinearGradient: - if (!m_linearGradientMaterial) - m_linearGradientMaterial.reset(QQuickPathItemGenericMaterialFactory::createLinearGradient(m_window, this)); - m_material = m_linearGradientMaterial.data(); - break; - default: - qWarning("Unknown material %d", m); - return; - } - - if (material() != m_material) - setMaterial(m_material); -} - -static bool q_supportsElementIndexUint(QSGRendererInterface::GraphicsApi api) -{ - static bool elementIndexUint = true; -#ifndef QT_NO_OPENGL - if (api == QSGRendererInterface::OpenGL) { - static bool elementIndexUintChecked = false; - if (!elementIndexUintChecked) { - elementIndexUintChecked = true; - QOpenGLContext *context = QOpenGLContext::currentContext(); - QScopedPointer dummyContext; - QScopedPointer dummySurface; - bool ok = true; - if (!context) { - dummyContext.reset(new QOpenGLContext); - dummyContext->create(); - context = dummyContext.data(); - dummySurface.reset(new QOffscreenSurface); - dummySurface->setFormat(context->format()); - dummySurface->create(); - ok = context->makeCurrent(dummySurface.data()); - } - if (ok) { - elementIndexUint = static_cast(context->functions())->hasOpenGLExtension( - QOpenGLExtensions::ElementIndexUint); - } - } - } -#else - Q_UNUSED(api); -#endif - return elementIndexUint; -} - -QQuickPathItemGenericRenderer::~QQuickPathItemGenericRenderer() -{ - for (VisualPathData &d : m_vp) { - if (d.pendingFill) - d.pendingFill->orphaned = true; - if (d.pendingStroke) - d.pendingStroke->orphaned = true; - } -} - -// sync, and so triangulation too, happens on the gui thread -// - except when async is set, in which case triangulation is moved to worker threads - -void QQuickPathItemGenericRenderer::beginSync(int totalCount) -{ - if (m_vp.count() != totalCount) { - m_vp.resize(totalCount); - m_accDirty |= DirtyList; - } - for (VisualPathData &d : m_vp) - d.syncDirty = 0; -} - -void QQuickPathItemGenericRenderer::setPath(int index, const QQuickPath *path) -{ - VisualPathData &d(m_vp[index]); - d.path = path ? path->path() : QPainterPath(); - d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom; -} - -void QQuickPathItemGenericRenderer::setJSPath(int index, const QQuickPathItemPath &path) -{ - VisualPathData &d(m_vp[index]); - d.path = path.toPainterPath(); - d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom; -} - -void QQuickPathItemGenericRenderer::setStrokeColor(int index, const QColor &color) -{ - VisualPathData &d(m_vp[index]); - d.strokeColor = colorToColor4ub(color); - d.syncDirty |= DirtyColor; -} - -void QQuickPathItemGenericRenderer::setStrokeWidth(int index, qreal w) -{ - VisualPathData &d(m_vp[index]); - d.strokeWidth = w; - if (w >= 0.0f) - d.pen.setWidthF(w); - d.syncDirty |= DirtyStrokeGeom; -} - -void QQuickPathItemGenericRenderer::setFillColor(int index, const QColor &color) -{ - VisualPathData &d(m_vp[index]); - d.fillColor = colorToColor4ub(color); - d.syncDirty |= DirtyColor; -} - -void QQuickPathItemGenericRenderer::setFillRule(int index, QQuickVisualPath::FillRule fillRule) -{ - VisualPathData &d(m_vp[index]); - d.fillRule = Qt::FillRule(fillRule); - d.syncDirty |= DirtyFillGeom; -} - -void QQuickPathItemGenericRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) -{ - VisualPathData &d(m_vp[index]); - d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); - d.pen.setMiterLimit(miterLimit); - d.syncDirty |= DirtyStrokeGeom; -} - -void QQuickPathItemGenericRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) -{ - VisualPathData &d(m_vp[index]); - d.pen.setCapStyle(Qt::PenCapStyle(capStyle)); - d.syncDirty |= DirtyStrokeGeom; -} - -void QQuickPathItemGenericRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, - qreal dashOffset, const QVector &dashPattern) -{ - VisualPathData &d(m_vp[index]); - d.pen.setStyle(Qt::PenStyle(strokeStyle)); - if (strokeStyle == QQuickVisualPath::DashLine) { - d.pen.setDashPattern(dashPattern); - d.pen.setDashOffset(dashOffset); - } - d.syncDirty |= DirtyStrokeGeom; -} - -void QQuickPathItemGenericRenderer::setFillGradient(int index, QQuickPathGradient *gradient) -{ - VisualPathData &d(m_vp[index]); - d.fillGradientActive = gradient != nullptr; - if (gradient) { - d.fillGradient.stops = gradient->sortedGradientStops(); - d.fillGradient.spread = gradient->spread(); - if (QQuickPathLinearGradient *g = qobject_cast(gradient)) { - d.fillGradient.start = QPointF(g->x1(), g->y1()); - d.fillGradient.end = QPointF(g->x2(), g->y2()); - } else { - Q_UNREACHABLE(); - } - } - d.syncDirty |= DirtyFillGradient; -} - -void QQuickPathItemFillRunnable::run() -{ - QQuickPathItemGenericRenderer::triangulateFill(path, fillColor, &fillVertices, &fillIndices, &indexType, supportsElementIndexUint); - emit done(this); -} - -void QQuickPathItemStrokeRunnable::run() -{ - QQuickPathItemGenericRenderer::triangulateStroke(path, pen, strokeColor, &strokeVertices, clipSize); - emit done(this); -} - -void QQuickPathItemGenericRenderer::setAsyncCallback(void (*callback)(void *), void *data) -{ - m_asyncCallback = callback; - m_asyncCallbackData = data; -} - -static QThreadPool *pathWorkThreadPool = nullptr; - -static void deletePathWorkThreadPool() -{ - delete pathWorkThreadPool; - pathWorkThreadPool = nullptr; -} - -void QQuickPathItemGenericRenderer::endSync(bool async) -{ - bool didKickOffAsync = false; - - for (int i = 0; i < m_vp.count(); ++i) { - VisualPathData &d(m_vp[i]); - 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; - } - - if (async && !pathWorkThreadPool) { - qAddPostRoutine(deletePathWorkThreadPool); - pathWorkThreadPool = new QThreadPool; - const int idealCount = QThread::idealThreadCount(); - pathWorkThreadPool->setMaxThreadCount(idealCount > 0 ? idealCount * 2 : 4); - } - - if ((d.syncDirty & DirtyFillGeom) && d.fillColor.a) { - d.path.setFillRule(d.fillRule); - if (m_api == QSGRendererInterface::Unknown) - m_api = m_item->window()->rendererInterface()->graphicsApi(); - if (async) { - QQuickPathItemFillRunnable *r = new QQuickPathItemFillRunnable; - r->setAutoDelete(false); - if (d.pendingFill) - d.pendingFill->orphaned = true; - d.pendingFill = r; - r->path = d.path; - r->fillColor = d.fillColor; - r->supportsElementIndexUint = q_supportsElementIndexUint(m_api); - // Unlikely in practice but in theory m_vp could be - // resized. Therefore, capture 'i' instead of 'd'. - QObject::connect(r, &QQuickPathItemFillRunnable::done, qApp, [this, i](QQuickPathItemFillRunnable *r) { - // Bail out when orphaned (meaning either another run was - // started after this one, or the renderer got destroyed). - if (!r->orphaned && i < m_vp.count()) { - VisualPathData &d(m_vp[i]); - d.fillVertices = r->fillVertices; - d.fillIndices = r->fillIndices; - d.indexType = r->indexType; - d.pendingFill = nullptr; - d.effectiveDirty |= DirtyFillGeom; - maybeUpdateAsyncItem(); - } - r->deleteLater(); - }); - didKickOffAsync = true; - pathWorkThreadPool->start(r); - } else { - triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices, &d.indexType, q_supportsElementIndexUint(m_api)); - } - } - - if ((d.syncDirty & DirtyStrokeGeom) && d.strokeWidth >= 0.0f && d.strokeColor.a) { - if (async) { - QQuickPathItemStrokeRunnable *r = new QQuickPathItemStrokeRunnable; - r->setAutoDelete(false); - if (d.pendingStroke) - d.pendingStroke->orphaned = true; - d.pendingStroke = r; - r->path = d.path; - r->pen = d.pen; - r->strokeColor = d.strokeColor; - r->clipSize = QSize(m_item->width(), m_item->height()); - QObject::connect(r, &QQuickPathItemStrokeRunnable::done, qApp, [this, i](QQuickPathItemStrokeRunnable *r) { - if (!r->orphaned && i < m_vp.count()) { - VisualPathData &d(m_vp[i]); - d.strokeVertices = r->strokeVertices; - d.pendingStroke = nullptr; - d.effectiveDirty |= DirtyStrokeGeom; - maybeUpdateAsyncItem(); - } - r->deleteLater(); - }); - didKickOffAsync = true; - pathWorkThreadPool->start(r); - } else { - triangulateStroke(d.path, d.pen, d.strokeColor, &d.strokeVertices, - QSize(m_item->width(), m_item->height())); - } - } - } - - if (!didKickOffAsync && async && m_asyncCallback) - m_asyncCallback(m_asyncCallbackData); -} - -void QQuickPathItemGenericRenderer::maybeUpdateAsyncItem() -{ - for (const VisualPathData &d : qAsConst(m_vp)) { - if (d.pendingFill || d.pendingStroke) - return; - } - m_accDirty |= DirtyFillGeom | DirtyStrokeGeom; - m_item->update(); - if (m_asyncCallback) - m_asyncCallback(m_asyncCallbackData); -} - -// the stroke/fill triangulation functions may be invoked either on the gui -// thread or some worker thread and must thus be self-contained. -void QQuickPathItemGenericRenderer::triangulateFill(const QPainterPath &path, - const Color4ub &fillColor, - VertexContainerType *fillVertices, - IndexContainerType *fillIndices, - QSGGeometry::Type *indexType, - bool supportsElementIndexUint) -{ - const QVectorPath &vp = qtVectorPathForPath(path); - - QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(TRI_SCALE, TRI_SCALE), 1, supportsElementIndexUint); - const int vertexCount = ts.vertices.count() / 2; // just a qreal vector with x,y hence the / 2 - fillVertices->resize(vertexCount); - ColoredVertex *vdst = reinterpret_cast(fillVertices->data()); - const qreal *vsrc = ts.vertices.constData(); - for (int i = 0; i < vertexCount; ++i) - vdst[i].set(vsrc[i * 2] / TRI_SCALE, vsrc[i * 2 + 1] / TRI_SCALE, fillColor); - - size_t indexByteSize; - if (ts.indices.type() == QVertexIndexVector::UnsignedShort) { - *indexType = QSGGeometry::UnsignedShortType; - // fillIndices is still QVector. Just resize to N/2 and pack - // the N quint16s into it. - fillIndices->resize(ts.indices.size() / 2); - indexByteSize = ts.indices.size() * sizeof(quint16); - } else { - *indexType = QSGGeometry::UnsignedIntType; - fillIndices->resize(ts.indices.size()); - indexByteSize = ts.indices.size() * sizeof(quint32); - } - memcpy(fillIndices->data(), ts.indices.data(), indexByteSize); -} - -void QQuickPathItemGenericRenderer::triangulateStroke(const QPainterPath &path, - const QPen &pen, - const Color4ub &strokeColor, - VertexContainerType *strokeVertices, - const QSize &clipSize) -{ - const QVectorPath &vp = qtVectorPathForPath(path); - const QRectF clip(QPointF(0, 0), clipSize); - const qreal inverseScale = 1.0 / TRI_SCALE; - - QTriangulatingStroker stroker; - stroker.setInvScale(inverseScale); - - if (pen.style() == Qt::SolidLine) { - stroker.process(vp, pen, clip, 0); - } else { - QDashedStrokeProcessor dashStroker; - dashStroker.setInvScale(inverseScale); - dashStroker.process(vp, pen, clip, 0); - QVectorPath dashStroke(dashStroker.points(), dashStroker.elementCount(), - dashStroker.elementTypes(), 0); - stroker.process(dashStroke, pen, clip, 0); - } - - if (!stroker.vertexCount()) { - strokeVertices->clear(); - return; - } - - const int vertexCount = stroker.vertexCount() / 2; // just a float vector with x,y hence the / 2 - strokeVertices->resize(vertexCount); - ColoredVertex *vdst = reinterpret_cast(strokeVertices->data()); - const float *vsrc = stroker.vertices(); - for (int i = 0; i < vertexCount; ++i) - vdst[i].set(vsrc[i * 2], vsrc[i * 2 + 1], strokeColor); -} - -void QQuickPathItemGenericRenderer::setRootNode(QQuickPathItemGenericNode *node) -{ - if (m_rootNode != node) { - m_rootNode = node; - m_accDirty |= DirtyList; - } -} - -// on the render thread with gui blocked -void QQuickPathItemGenericRenderer::updateNode() -{ - if (!m_rootNode || !m_accDirty) - return; - -// [ 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); - } - - QQuickPathItemGenericNode *node = *nodePtr; - - if (m_accDirty & DirtyList) - d.effectiveDirty |= DirtyFillGeom | DirtyStrokeGeom | DirtyColor | DirtyFillGradient; - - if (!d.effectiveDirty) { - prevNode = node; - nodePtr = &node->m_next; - 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 |= DirtyFillGeom; - } - - 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 |= DirtyStrokeGeom; - } - - updateFillNode(&d, node); - updateStrokeNode(&d, node); - - d.effectiveDirty = 0; - - prevNode = node; - nodePtr = &node->m_next; - } - - if (*nodePtr && prevNode) { - prevNode->removeChildNode(*nodePtr); - delete *nodePtr; - *nodePtr = nullptr; - } - - m_accDirty = 0; -} - -void QQuickPathItemGenericRenderer::updateShadowDataInNode(VisualPathData *d, QQuickPathItemGenericStrokeFillNode *n) -{ - if (d->fillGradientActive) { - if (d->effectiveDirty & DirtyFillGradient) - n->m_fillGradient = d->fillGradient; - } -} - -void QQuickPathItemGenericRenderer::updateFillNode(VisualPathData *d, QQuickPathItemGenericNode *node) -{ - if (!node->m_fillNode) - return; - if (!(d->effectiveDirty & (DirtyFillGeom | DirtyColor | DirtyFillGradient))) - return; - - // Make a copy of the data that will be accessed by the material on - // the render thread. This must be done even when we bail out below. - QQuickPathItemGenericStrokeFillNode *n = node->m_fillNode; - updateShadowDataInNode(d, n); - - QSGGeometry *g = n->m_geometry; - if (d->fillVertices.isEmpty()) { - if (g->vertexCount() || g->indexCount()) { - g->allocate(0, 0); - n->markDirty(QSGNode::DirtyGeometry); - } - return; - } - - if (d->fillGradientActive) { - n->activateMaterial(QQuickPathItemGenericStrokeFillNode::MatLinearGradient); - if (d->effectiveDirty & DirtyFillGradient) { - // 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 (!(d->effectiveDirty & DirtyFillGeom)) - return; - } - } else { - n->activateMaterial(QQuickPathItemGenericStrokeFillNode::MatSolidColor); - // fast path for updating only color values when no change in vertex positions - if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyFillGeom)) { - ColoredVertex *vdst = reinterpret_cast(g->vertexData()); - for (int i = 0; i < g->vertexCount(); ++i) - vdst[i].set(vdst[i].x, vdst[i].y, d->fillColor); - n->markDirty(QSGNode::DirtyGeometry); - return; - } - } - - const int indexCount = d->indexType == QSGGeometry::UnsignedShortType - ? d->fillIndices.count() * 2 : d->fillIndices.count(); - if (g->indexType() != d->indexType) { - g = new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), - d->fillVertices.count(), indexCount, d->indexType); - n->setGeometry(g); - delete n->m_geometry; - n->m_geometry = g; - } else { - g->allocate(d->fillVertices.count(), indexCount); - } - g->setDrawingMode(QSGGeometry::DrawTriangles); - 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(VisualPathData *d, QQuickPathItemGenericNode *node) -{ - if (!node->m_strokeNode) - return; - if (!(d->effectiveDirty & (DirtyStrokeGeom | DirtyColor))) - return; - - QQuickPathItemGenericStrokeFillNode *n = node->m_strokeNode; - QSGGeometry *g = n->m_geometry; - if (d->strokeVertices.isEmpty()) { - if (g->vertexCount() || g->indexCount()) { - g->allocate(0, 0); - n->markDirty(QSGNode::DirtyGeometry); - } - return; - } - - n->markDirty(QSGNode::DirtyGeometry); - - // Async loading runs update once, bails out above, then updates again once - // ready. Set the material dirty then. This is in-line with fill where the - // first activateMaterial() achieves the same. - if (!g->vertexCount()) - n->markDirty(QSGNode::DirtyMaterial); - - if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyStrokeGeom)) { - ColoredVertex *vdst = reinterpret_cast(g->vertexData()); - for (int i = 0; i < g->vertexCount(); ++i) - vdst[i].set(vdst[i].x, vdst[i].y, d->strokeColor); - return; - } - - g->allocate(d->strokeVertices.count(), 0); - g->setDrawingMode(QSGGeometry::DrawTriangleStrip); - memcpy(g->vertexData(), d->strokeVertices.constData(), g->vertexCount() * g->sizeOfVertex()); -} - -QSGMaterial *QQuickPathItemGenericMaterialFactory::createVertexColor(QQuickWindow *window) -{ - QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); - -#ifndef QT_NO_OPENGL - if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... - return new QSGVertexColorMaterial; -#endif - - qWarning("Vertex-color material: Unsupported graphics API %d", api); - return nullptr; -} - -QSGMaterial *QQuickPathItemGenericMaterialFactory::createLinearGradient(QQuickWindow *window, - QQuickPathItemGenericStrokeFillNode *node) -{ - QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); - -#ifndef QT_NO_OPENGL - if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... - return new QQuickPathItemLinearGradientMaterial(node); -#endif - - qWarning("Linear gradient material: Unsupported graphics API %d", api); - return nullptr; -} - -#ifndef QT_NO_OPENGL - -QSGMaterialType QQuickPathItemLinearGradientShader::type; - -QQuickPathItemLinearGradientShader::QQuickPathItemLinearGradientShader() -{ - setShaderSourceFile(QOpenGLShader::Vertex, - QStringLiteral(":/qt-project.org/items/shaders/lineargradient.vert")); - setShaderSourceFile(QOpenGLShader::Fragment, - QStringLiteral(":/qt-project.org/items/shaders/lineargradient.frag")); -} - -void QQuickPathItemLinearGradientShader::initialize() -{ - m_opacityLoc = program()->uniformLocation("opacity"); - m_matrixLoc = program()->uniformLocation("matrix"); - m_gradStartLoc = program()->uniformLocation("gradStart"); - m_gradEndLoc = program()->uniformLocation("gradEnd"); -} - -void QQuickPathItemLinearGradientShader::updateState(const RenderState &state, QSGMaterial *mat, QSGMaterial *) -{ - QQuickPathItemLinearGradientMaterial *m = static_cast(mat); - - if (state.isOpacityDirty()) - program()->setUniformValue(m_opacityLoc, state.opacity()); - - if (state.isMatrixDirty()) - program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); - - QQuickPathItemGenericStrokeFillNode *node = m->node(); - program()->setUniformValue(m_gradStartLoc, QVector2D(node->m_fillGradient.start)); - program()->setUniformValue(m_gradEndLoc, QVector2D(node->m_fillGradient.end)); - - QSGTexture *tx = QQuickPathItemGradientCache::currentCache()->get(node->m_fillGradient); - tx->bind(); -} - -char const *const *QQuickPathItemLinearGradientShader::attributeNames() const -{ - static const char *const attr[] = { "vertexCoord", "vertexColor", nullptr }; - return attr; -} - -int QQuickPathItemLinearGradientMaterial::compare(const QSGMaterial *other) const -{ - Q_ASSERT(other && type() == other->type()); - const QQuickPathItemLinearGradientMaterial *m = static_cast(other); - - QQuickPathItemGenericStrokeFillNode *a = node(); - QQuickPathItemGenericStrokeFillNode *b = m->node(); - Q_ASSERT(a && b); - if (a == b) - return 0; - - const QQuickPathItemGradientCache::GradientDesc *ga = &a->m_fillGradient; - const QQuickPathItemGradientCache::GradientDesc *gb = &b->m_fillGradient; - - if (int d = ga->spread - gb->spread) - return d; - - if (int d = ga->start.x() - gb->start.x()) - return d; - if (int d = ga->start.y() - gb->start.y()) - return d; - if (int d = ga->end.x() - gb->end.x()) - return d; - if (int d = ga->end.y() - gb->end.y()) - return d; - - if (int d = ga->stops.count() - gb->stops.count()) - return d; - - for (int i = 0; i < ga->stops.count(); ++i) { - if (int d = ga->stops[i].first - gb->stops[i].first) - return d; - if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba()) - return d; - } - - return 0; -} - -#endif // QT_NO_OPENGL - -QT_END_NAMESPACE diff --git a/src/imports/pathitem/qquickpathitemgenericrenderer_p.h b/src/imports/pathitem/qquickpathitemgenericrenderer_p.h deleted file mode 100644 index 70a9e88d2f..0000000000 --- a/src/imports/pathitem/qquickpathitemgenericrenderer_p.h +++ /dev/null @@ -1,303 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKPATHITEMGENERICRENDERER_P_H -#define QQUICKPATHITEMGENERICRENDERER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of a number of Qt sources files. This header file may change from -// version to version without notice, or even be removed. -// -// We mean it. -// - -#include "qquickpathitem_p_p.h" -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QQuickPathItemGenericNode; -class QQuickPathItemGenericStrokeFillNode; -class QQuickPathItemFillRunnable; -class QQuickPathItemStrokeRunnable; - -class QQuickPathItemGenericRenderer : public QQuickAbstractPathRenderer -{ -public: - enum Dirty { - DirtyFillGeom = 0x01, - DirtyStrokeGeom = 0x02, - DirtyColor = 0x04, - DirtyFillGradient = 0x08, - DirtyList = 0x10 // only for accDirty - }; - - QQuickPathItemGenericRenderer(QQuickItem *item) - : m_item(item), - m_api(QSGRendererInterface::Unknown), - m_rootNode(nullptr), - m_accDirty(0), - m_asyncCallback(nullptr) - { } - ~QQuickPathItemGenericRenderer(); - - void beginSync(int totalCount) override; - void setPath(int index, const QQuickPath *path) override; - void setJSPath(int index, const QQuickPathItemPath &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 &dashPattern) override; - void setFillGradient(int index, QQuickPathGradient *gradient) override; - void endSync(bool async) override; - void setAsyncCallback(void (*)(void *), void *) override; - Flags flags() const override { return SupportsAsync; } - - void updateNode() override; - - void setRootNode(QQuickPathItemGenericNode *node); - - struct Color4ub { unsigned char r, g, b, a; }; - typedef QVector VertexContainerType; - typedef QVector IndexContainerType; - - static void triangulateFill(const QPainterPath &path, - const Color4ub &fillColor, - VertexContainerType *fillVertices, - IndexContainerType *fillIndices, - QSGGeometry::Type *indexType, - bool supportsElementIndexUint); - static void triangulateStroke(const QPainterPath &path, - const QPen &pen, - const Color4ub &strokeColor, - VertexContainerType *strokeVertices, - const QSize &clipSize); - -private: - void maybeUpdateAsyncItem(); - - struct VisualPathData { - float strokeWidth; - QPen pen; - Color4ub strokeColor; - Color4ub fillColor; - Qt::FillRule fillRule; - QPainterPath path; - bool fillGradientActive; - QQuickPathItemGradientCache::GradientDesc fillGradient; - VertexContainerType fillVertices; - IndexContainerType fillIndices; - QSGGeometry::Type indexType; - VertexContainerType strokeVertices; - int syncDirty; - int effectiveDirty = 0; - QQuickPathItemFillRunnable *pendingFill = nullptr; - QQuickPathItemStrokeRunnable *pendingStroke = nullptr; - }; - - void updateShadowDataInNode(VisualPathData *d, QQuickPathItemGenericStrokeFillNode *n); - void updateFillNode(VisualPathData *d, QQuickPathItemGenericNode *node); - void updateStrokeNode(VisualPathData *d, QQuickPathItemGenericNode *node); - - QQuickItem *m_item; - QSGRendererInterface::GraphicsApi m_api; - QQuickPathItemGenericNode *m_rootNode; - QVector m_vp; - int m_accDirty; - void (*m_asyncCallback)(void *); - void *m_asyncCallbackData; -}; - -class QQuickPathItemFillRunnable : public QObject, public QRunnable -{ - Q_OBJECT - -public: - void run() override; - - bool orphaned = false; - - // input - QPainterPath path; - QQuickPathItemGenericRenderer::Color4ub fillColor; - bool supportsElementIndexUint; - - // output - QQuickPathItemGenericRenderer::VertexContainerType fillVertices; - QQuickPathItemGenericRenderer::IndexContainerType fillIndices; - QSGGeometry::Type indexType; - -Q_SIGNALS: - void done(QQuickPathItemFillRunnable *self); -}; - -class QQuickPathItemStrokeRunnable : public QObject, public QRunnable -{ - Q_OBJECT - -public: - void run() override; - - bool orphaned = false; - - // input - QPainterPath path; - QPen pen; - QQuickPathItemGenericRenderer::Color4ub strokeColor; - QSize clipSize; - - // output - QQuickPathItemGenericRenderer::VertexContainerType strokeVertices; - -Q_SIGNALS: - void done(QQuickPathItemStrokeRunnable *self); -}; - -class QQuickPathItemGenericStrokeFillNode : public QSGGeometryNode -{ -public: - QQuickPathItemGenericStrokeFillNode(QQuickWindow *window); - ~QQuickPathItemGenericStrokeFillNode(); - - enum Material { - MatSolidColor, - MatLinearGradient - }; - - void activateMaterial(Material m); - - QQuickWindow *window() const { return m_window; } - - // shadow data for custom materials - QQuickPathItemGradientCache::GradientDesc m_fillGradient; - -private: - QSGGeometry *m_geometry; - QQuickWindow *m_window; - QSGMaterial *m_material; - QScopedPointer m_solidColorMaterial; - QScopedPointer m_linearGradientMaterial; - - friend class QQuickPathItemGenericRenderer; -}; - -class QQuickPathItemGenericNode : public QSGNode -{ -public: - 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, QQuickPathItemGenericStrokeFillNode *node); -}; - -#ifndef QT_NO_OPENGL - -class QQuickPathItemLinearGradientShader : public QSGMaterialShader -{ -public: - QQuickPathItemLinearGradientShader(); - - void initialize() override; - void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; - char const *const *attributeNames() const override; - - static QSGMaterialType type; - -private: - int m_opacityLoc; - int m_matrixLoc; - int m_gradStartLoc; - int m_gradEndLoc; -}; - -class QQuickPathItemLinearGradientMaterial : public QSGMaterial -{ -public: - QQuickPathItemLinearGradientMaterial(QQuickPathItemGenericStrokeFillNode *node) - : m_node(node) - { - // Passing RequiresFullMatrix is essential in order to prevent the - // batch renderer from baking in simple, translate-only transforms into - // the vertex data. The shader will rely on the fact that - // vertexCoord.xy is the PathItem-space coordinate and so no modifications - // are welcome. - setFlag(Blending | RequiresFullMatrix); - } - - QSGMaterialType *type() const override - { - return &QQuickPathItemLinearGradientShader::type; - } - - int compare(const QSGMaterial *other) const override; - - QSGMaterialShader *createShader() const override - { - return new QQuickPathItemLinearGradientShader; - } - - QQuickPathItemGenericStrokeFillNode *node() const { return m_node; } - -private: - QQuickPathItemGenericStrokeFillNode *m_node; -}; - -#endif // QT_NO_OPENGL - -QT_END_NAMESPACE - -#endif // QQUICKPATHITEMGENERICRENDERER_P_H diff --git a/src/imports/pathitem/qquickpathitemnvprrenderer.cpp b/src/imports/pathitem/qquickpathitemnvprrenderer.cpp deleted file mode 100644 index f8504f9985..0000000000 --- a/src/imports/pathitem/qquickpathitemnvprrenderer.cpp +++ /dev/null @@ -1,923 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquickpathitemnvprrenderer_p.h" -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -void QQuickPathItemNvprRenderer::beginSync(int totalCount) -{ - if (m_vp.count() != totalCount) { - m_vp.resize(totalCount); - m_accDirty |= DirtyList; - } -} - -void QQuickPathItemNvprRenderer::setPath(int index, const QQuickPath *path) -{ - VisualPathGuiData &d(m_vp[index]); - convertPath(path, &d); - d.dirty |= DirtyPath; - m_accDirty |= DirtyPath; -} - -void QQuickPathItemNvprRenderer::setJSPath(int index, const QQuickPathItemPath &path) -{ - VisualPathGuiData &d(m_vp[index]); - convertJSPath(path, &d); - d.dirty |= DirtyPath; - m_accDirty |= DirtyPath; -} - -void QQuickPathItemNvprRenderer::setStrokeColor(int index, const QColor &color) -{ - VisualPathGuiData &d(m_vp[index]); - d.strokeColor = color; - d.dirty |= DirtyStyle; - m_accDirty |= DirtyStyle; -} - -void QQuickPathItemNvprRenderer::setStrokeWidth(int index, qreal w) -{ - VisualPathGuiData &d(m_vp[index]); - d.strokeWidth = w; - d.dirty |= DirtyStyle; - m_accDirty |= DirtyStyle; -} - -void QQuickPathItemNvprRenderer::setFillColor(int index, const QColor &color) -{ - VisualPathGuiData &d(m_vp[index]); - d.fillColor = color; - d.dirty |= DirtyStyle; - m_accDirty |= DirtyStyle; -} - -void QQuickPathItemNvprRenderer::setFillRule(int index, QQuickVisualPath::FillRule fillRule) -{ - VisualPathGuiData &d(m_vp[index]); - d.fillRule = fillRule; - d.dirty |= DirtyFillRule; - m_accDirty |= DirtyFillRule; -} - -void QQuickPathItemNvprRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) -{ - VisualPathGuiData &d(m_vp[index]); - d.joinStyle = joinStyle; - d.miterLimit = miterLimit; - d.dirty |= DirtyStyle; - m_accDirty |= DirtyStyle; -} - -void QQuickPathItemNvprRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) -{ - VisualPathGuiData &d(m_vp[index]); - d.capStyle = capStyle; - d.dirty |= DirtyStyle; - m_accDirty |= DirtyStyle; -} - -void QQuickPathItemNvprRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, - qreal dashOffset, const QVector &dashPattern) -{ - 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(int index, QQuickPathGradient *gradient) -{ - VisualPathGuiData &d(m_vp[index]); - d.fillGradientActive = gradient != nullptr; - if (gradient) { - d.fillGradient.stops = gradient->sortedGradientStops(); - d.fillGradient.spread = gradient->spread(); - if (QQuickPathLinearGradient *g = qobject_cast(gradient)) { - d.fillGradient.start = QPointF(g->x1(), g->y1()); - d.fillGradient.end = QPointF(g->x2(), g->y2()); - } else { - Q_UNREACHABLE(); - } - } - d.dirty |= DirtyFillGradient; - m_accDirty |= DirtyFillGradient; -} - -void QQuickPathItemNvprRenderer::endSync(bool) -{ -} - -void QQuickPathItemNvprRenderer::setNode(QQuickPathItemNvprRenderNode *node) -{ - if (m_node != node) { - m_node = node; - m_accDirty |= DirtyList; - } -} - -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) { - static struct { GLubyte cmd; const char *s; int coordCount; } nameTab[] = { - { GL_MOVE_TO_NV, "moveTo", 2 }, - { GL_LINE_TO_NV, "lineTo", 2 }, - { GL_QUADRATIC_CURVE_TO_NV, "quadTo", 4 }, - { GL_CUBIC_CURVE_TO_NV, "cubicTo", 6 }, - { GL_LARGE_CW_ARC_TO_NV, "arcTo-large-CW", 5 }, - { GL_LARGE_CCW_ARC_TO_NV, "arcTo-large-CCW", 5 }, - { GL_SMALL_CW_ARC_TO_NV, "arcTo-small-CW", 5 }, - { GL_SMALL_CCW_ARC_TO_NV, "arcTo-small-CCW", 5 }, - { GL_CLOSE_PATH_NV, "closePath", 0 } }; - for (size_t i = 0; i < sizeof(nameTab) / sizeof(nameTab[0]); ++i) { - if (nameTab[i].cmd == cmd) { - QByteArray cs; - for (int j = 0; j < nameTab[i].coordCount; ++j) { - cs.append(QByteArray::number(path.coord[ci++])); - cs.append(' '); - } - debug << "\n " << nameTab[i].s << " " << cs; - break; - } - } - } - return debug; -} - -static inline void appendCoords(QVector *v, QQuickCurve *c, QPointF *pos) -{ - QPointF p(c->hasRelativeX() ? pos->x() + c->relativeX() : c->x(), - c->hasRelativeY() ? pos->y() + c->relativeY() : c->y()); - v->append(p.x()); - v->append(p.y()); - *pos = p; -} - -static inline void appendControlCoords(QVector *v, QQuickPathQuad *c, const QPointF &pos) -{ - QPointF p(c->hasRelativeControlX() ? pos.x() + c->relativeControlX() : c->controlX(), - c->hasRelativeControlY() ? pos.y() + c->relativeControlY() : c->controlY()); - v->append(p.x()); - v->append(p.y()); -} - -static inline void appendControl1Coords(QVector *v, QQuickPathCubic *c, const QPointF &pos) -{ - QPointF p(c->hasRelativeControl1X() ? pos.x() + c->relativeControl1X() : c->control1X(), - c->hasRelativeControl1Y() ? pos.y() + c->relativeControl1Y() : c->control1Y()); - v->append(p.x()); - v->append(p.y()); -} - -static inline void appendControl2Coords(QVector *v, QQuickPathCubic *c, const QPointF &pos) -{ - QPointF p(c->hasRelativeControl2X() ? pos.x() + c->relativeControl2X() : c->control2X(), - c->hasRelativeControl2Y() ? pos.y() + c->relativeControl2Y() : c->control2Y()); - v->append(p.x()); - v->append(p.y()); -} - -void QQuickPathItemNvprRenderer::convertPath(const QQuickPath *path, VisualPathGuiData *d) -{ - d->path = NvprPath(); - if (!path) - return; - - const QList &pp(QQuickPathPrivate::get(path)->_pathElements); - if (pp.isEmpty()) - return; - - 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(e)) { - d->path.cmd.append(GL_MOVE_TO_NV); - appendCoords(&d->path.coord, o, &pos); - startPos = pos; - } else if (QQuickPathLine *o = qobject_cast(e)) { - d->path.cmd.append(GL_LINE_TO_NV); - appendCoords(&d->path.coord, o, &pos); - } else if (QQuickPathQuad *o = qobject_cast(e)) { - 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(e)) { - 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(e)) { - const bool sweepFlag = o->direction() == QQuickPathArc::Clockwise; // maps to CCW, not a typo - GLenum cmd; - if (o->useLargeArc()) - cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV; - else - cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV; - 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(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; - } - } - - // 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); -} - -void QQuickPathItemNvprRenderer::convertJSPath(const QQuickPathItemPath &path, VisualPathGuiData *d) -{ - d->path = NvprPath(); - if (path.cmd.isEmpty()) - return; - - QPointF startPos(0, 0); - QPointF pos(startPos); - int coordIdx = 0; - - for (QQuickPathItemPath::Command cmd : path.cmd) { - switch (cmd) { - case QQuickPathItemPath::MoveTo: - d->path.cmd.append(GL_MOVE_TO_NV); - pos = QPointF(path.coords[coordIdx], path.coords[coordIdx + 1]); - startPos = pos; - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - coordIdx += 2; - break; - case QQuickPathItemPath::LineTo: - d->path.cmd.append(GL_LINE_TO_NV); - pos = QPointF(path.coords[coordIdx], path.coords[coordIdx + 1]); - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - coordIdx += 2; - break; - case QQuickPathItemPath::QuadTo: - d->path.cmd.append(GL_QUADRATIC_CURVE_TO_NV); - d->path.coord.append(path.coords[coordIdx]); - d->path.coord.append(path.coords[coordIdx + 1]); - pos = QPointF(path.coords[coordIdx + 2], path.coords[coordIdx + 3]); - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - coordIdx += 4; - break; - case QQuickPathItemPath::CubicTo: - d->path.cmd.append(GL_CUBIC_CURVE_TO_NV); - d->path.coord.append(path.coords[coordIdx]); - d->path.coord.append(path.coords[coordIdx + 1]); - d->path.coord.append(path.coords[coordIdx + 2]); - d->path.coord.append(path.coords[coordIdx + 3]); - pos = QPointF(path.coords[coordIdx + 4], path.coords[coordIdx + 5]); - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - coordIdx += 6; - break; - case QQuickPathItemPath::ArcTo: - { - const bool sweepFlag = !qFuzzyIsNull(path.coords[coordIdx + 5]); - const bool useLargeArc = !qFuzzyIsNull(path.coords[coordIdx + 6]); - GLenum cmd; - if (useLargeArc) - cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV; - else - cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV; - d->path.cmd.append(cmd); - d->path.coord.append(path.coords[coordIdx]); // rx - d->path.coord.append(path.coords[coordIdx + 1]); // ry - d->path.coord.append(path.coords[coordIdx + 2]); // xrot - pos = QPointF(path.coords[coordIdx + 3], path.coords[coordIdx + 4]); - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - coordIdx += 7; - } - break; - default: - qWarning("Unknown JS path command: %d", cmd); - break; - } - } - - if (pos == startPos) - d->path.cmd.append(GL_CLOSE_PATH_NV); -} - -static inline QVector4D qsg_premultiply(const QColor &c, float globalOpacity) -{ - const float o = c.alphaF() * globalOpacity; - return QVector4D(c.redF() * o, c.greenF() * o, c.blueF() * o, o); -} - -void QQuickPathItemNvprRenderer::updateNode() -{ - // Called on the render thread with gui blocked -> update the node with its - // own copy of all relevant data. - - if (!m_accDirty) - return; - - 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 (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 (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 (dirty & DirtyFillGradient) { - dst.fillGradientActive = src.fillGradientActive; - if (src.fillGradientActive) - dst.fillGradient = src.fillGradient; - } - } - - m_node->markDirty(QSGNode::DirtyMaterial); - m_accDirty = 0; -} - -bool QQuickPathItemNvprRenderNode::nvprInited = false; -QQuickNvprFunctions QQuickPathItemNvprRenderNode::nvpr; -QQuickNvprMaterialManager QQuickPathItemNvprRenderNode::mtlmgr; - -QQuickPathItemNvprRenderNode::~QQuickPathItemNvprRenderNode() -{ - releaseResources(); -} - -void QQuickPathItemNvprRenderNode::releaseResources() -{ - 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(); -} - -void QQuickNvprMaterialManager::create(QQuickNvprFunctions *nvpr) -{ - m_nvpr = nvpr; -} - -void QQuickNvprMaterialManager::releaseResources() -{ - QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); - for (MaterialDesc &mtl : m_materials) { - if (mtl.ppl) { - f->glDeleteProgramPipelines(1, &mtl.ppl); - mtl = MaterialDesc(); - } - } -} - -QQuickNvprMaterialManager::MaterialDesc *QQuickNvprMaterialManager::activateMaterial(Material m) -{ - QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); - MaterialDesc &mtl(m_materials[m]); - - if (!mtl.ppl) { - if (m == MatSolid) { - static const char *fragSrc = - "#version 310 es\n" - "precision highp float;\n" - "out vec4 fragColor;\n" - "uniform vec4 color;\n" - "uniform float opacity;\n" - "void main() {\n" - " fragColor = color * opacity;\n" - "}\n"; - if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { - qWarning("NVPR: Failed to create shader pipeline for solid fill"); - return nullptr; - } - Q_ASSERT(mtl.ppl && mtl.prg); - mtl.uniLoc[0] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "color"); - Q_ASSERT(mtl.uniLoc[0] >= 0); - mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); - Q_ASSERT(mtl.uniLoc[1] >= 0); - } else if (m == MatLinearGradient) { - static const char *fragSrc = - "#version 310 es\n" - "precision highp float;\n" - "layout(location = 0) in vec2 uv;" - "uniform float opacity;\n" - "uniform sampler2D gradTab;\n" - "uniform vec2 gradStart;\n" - "uniform vec2 gradEnd;\n" - "out vec4 fragColor;\n" - "void main() {\n" - " vec2 gradVec = gradEnd - gradStart;\n" - " float gradTabIndex = dot(gradVec, uv - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y);\n" - " fragColor = texture(gradTab, vec2(gradTabIndex, 0.5)) * opacity;\n" - "}\n"; - if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { - qWarning("NVPR: Failed to create shader pipeline for linear gradient"); - return nullptr; - } - Q_ASSERT(mtl.ppl && mtl.prg); - mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); - Q_ASSERT(mtl.uniLoc[1] >= 0); - mtl.uniLoc[2] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradStart"); - Q_ASSERT(mtl.uniLoc[2] >= 0); - mtl.uniLoc[3] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradEnd"); - Q_ASSERT(mtl.uniLoc[3] >= 0); - } else { - Q_UNREACHABLE(); - } - } - - f->glBindProgramPipeline(mtl.ppl); - - return &mtl; -} - -void QQuickPathItemNvprRenderNode::updatePath(VisualPathRenderData *d) -{ - 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()); - } - } - - 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 (d->dirty & QQuickPathItemNvprRenderer::DirtyDash) { - nvpr.pathParameterf(d->path, GL_PATH_DASH_OFFSET_NV, d->dashOffset); - // count == 0 -> no dash - nvpr.pathDashArray(d->path, d->dashPattern.count(), d->dashPattern.constData()); - } - - if (d->dirty) - d->fallbackValid = false; -} - -void QQuickPathItemNvprRenderNode::renderStroke(VisualPathRenderData *d, int strokeStencilValue, int writeMask) -{ - QQuickNvprMaterialManager::MaterialDesc *mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); - f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], - d->strokeColor.x(), d->strokeColor.y(), d->strokeColor.z(), d->strokeColor.w()); - f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); - - nvpr.stencilThenCoverStrokePath(d->path, strokeStencilValue, writeMask, GL_CONVEX_HULL_NV); -} - -void QQuickPathItemNvprRenderNode::renderFill(VisualPathRenderData *d) -{ - QQuickNvprMaterialManager::MaterialDesc *mtl = nullptr; - if (d->fillGradientActive) { - mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatLinearGradient); - 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 - // we need since the gradient's start and stop are in that space too. - GLfloat coeff[6] = { 1, 0, 0, - 0, 1, 0 }; - nvpr.programPathFragmentInputGen(mtl->prg, 0, GL_OBJECT_LINEAR_NV, 2, coeff); - f->glProgramUniform2f(mtl->prg, mtl->uniLoc[2], 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], - 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(d->path, d->fillRule, writeMask, GL_BOUNDING_BOX_NV); -} - -void QQuickPathItemNvprRenderNode::renderOffscreenFill(VisualPathRenderData *d) -{ - if (d->fallbackValid && d->fallbackFbo) - return; - - GLfloat bb[4]; - nvpr.getPathParameterfv(d->path, GL_PATH_STROKE_BOUNDING_BOX_NV, bb); - QSize sz = QSizeF(bb[2] - bb[0] + 1, bb[3] - bb[1] + 1).toSize(); - d->fallbackSize = QSize(qMax(32, sz.width()), qMax(32, sz.height())); - d->fallbackTopLeft = QPointF(bb[0], bb[1]); - - if (d->fallbackFbo && d->fallbackFbo->size() != d->fallbackSize) { - delete d->fallbackFbo; - d->fallbackFbo = nullptr; - } - if (!d->fallbackFbo) - d->fallbackFbo = new QOpenGLFramebufferObject(d->fallbackSize, QOpenGLFramebufferObject::CombinedDepthStencil); - if (!d->fallbackFbo->bind()) - return; - - GLint prevViewport[4]; - f->glGetIntegerv(GL_VIEWPORT, prevViewport); - - f->glViewport(0, 0, d->fallbackSize.width(), d->fallbackSize.height()); - f->glDisable(GL_DEPTH_TEST); - f->glClearColor(0, 0, 0, 0); - f->glClearStencil(0); - f->glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); - f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - - QMatrix4x4 mv; - mv.translate(-d->fallbackTopLeft.x(), -d->fallbackTopLeft.y()); - nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, mv.constData()); - QMatrix4x4 proj; - proj.ortho(0, d->fallbackSize.width(), d->fallbackSize.height(), 0, 1, -1); - nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, proj.constData()); - - renderFill(d); - - d->fallbackFbo->release(); - f->glEnable(GL_DEPTH_TEST); - f->glViewport(prevViewport[0], prevViewport[1], prevViewport[2], prevViewport[3]); - - d->fallbackValid = true; -} - -void QQuickPathItemNvprRenderNode::setupStencilForCover(bool stencilClip, int sv) -{ - if (!stencilClip) { - // Assume stencil buffer is cleared to 0 for each frame. - // Within the frame dppass=GL_ZERO for glStencilOp ensures stencil is reset and so no need to clear. - f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); - f->glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO); - } else { - f->glStencilFunc(GL_LESS, sv, 0xFF); // pass if (sv & 0xFF) < (stencil_value & 0xFF) - f->glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); // dppass: replace with the original value (clip's stencil ref value) - } -} - -void QQuickPathItemNvprRenderNode::render(const RenderState *state) -{ - f = QOpenGLContext::currentContext()->extraFunctions(); - - if (!nvprInited) { - if (!nvpr.create()) { - qWarning("NVPR init failed"); - return; - } - mtlmgr.create(&nvpr); - nvprInited = true; - } - - f->glUseProgram(0); - f->glStencilMask(~0); - f->glEnable(GL_STENCIL_TEST); - - const bool stencilClip = state->stencilEnabled(); - // when true, the stencil buffer already has a clip path with a ref value of sv - const int sv = state->stencilValue(); - const bool hasScissor = state->scissorEnabled(); - - if (hasScissor) { - // scissor rect is already set, just enable scissoring - f->glEnable(GL_SCISSOR_TEST); - } - - // 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); - - bool reloadMatrices = true; - - for (VisualPathRenderData &d : m_vp) { - updatePath(&d); - - const bool hasFill = d.hasFill(); - const bool hasStroke = d.hasStroke(); - - 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... - if (hasScissor) - f->glDisable(GL_SCISSOR_TEST); - renderOffscreenFill(&d); - reloadMatrices = true; - if (hasScissor) - f->glEnable(GL_SCISSOR_TEST); - } - - if (reloadMatrices) { - reloadMatrices = false; - nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, matrix()->constData()); - nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, state->projectionMatrix()->constData()); - } - - // 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); - QMatrix4x4 mv = *matrix(); - mv.translate(d.fallbackTopLeft.x(), d.fallbackTopLeft.y()); - m_fallbackBlitter.texturedQuad(d.fallbackFbo->texture(), d.fallbackFbo->size(), - *state->projectionMatrix(), mv, - 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); - } - - renderStroke(&d, strokeStencilValue, writeMask); - } - - if (stencilClip) - nvpr.pathStencilFunc(GL_ALWAYS, 0, ~0); - - d.dirty = 0; - } - - f->glBindProgramPipeline(0); -} - -QSGRenderNode::StateFlags QQuickPathItemNvprRenderNode::changedStates() const -{ - return BlendState | StencilState | DepthState | ScissorState; -} - -QSGRenderNode::RenderingFlags QQuickPathItemNvprRenderNode::flags() const -{ - return DepthAwareRendering; // avoid hitting the less optimal no-opaque-batch path in the renderer -} - -bool QQuickPathItemNvprRenderNode::isSupported() -{ - static const bool nvprDisabled = qEnvironmentVariableIntValue("QT_NO_NVPR") != 0; - return !nvprDisabled && QQuickNvprFunctions::isSupported(); -} - -bool QQuickNvprBlitter::create() -{ - if (isCreated()) - destroy(); - - m_program = new QOpenGLShaderProgram; - if (QOpenGLContext::currentContext()->format().profile() == QSurfaceFormat::CoreProfile) { - m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/shadereffect_core.vert")); - m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/shadereffect_core.frag")); - } else { - m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/shadereffect.vert")); - m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/shadereffect.frag")); - } - m_program->bindAttributeLocation("qt_Vertex", 0); - m_program->bindAttributeLocation("qt_MultiTexCoord0", 1); - if (!m_program->link()) - return false; - - m_matrixLoc = m_program->uniformLocation("qt_Matrix"); - m_opacityLoc = m_program->uniformLocation("qt_Opacity"); - - m_buffer = new QOpenGLBuffer; - if (!m_buffer->create()) - return false; - m_buffer->bind(); - m_buffer->allocate(4 * sizeof(GLfloat) * 6); - m_buffer->release(); - - return true; -} - -void QQuickNvprBlitter::destroy() -{ - if (m_program) { - delete m_program; - m_program = nullptr; - } - if (m_buffer) { - delete m_buffer; - m_buffer = nullptr; - } -} - -void QQuickNvprBlitter::texturedQuad(GLuint textureId, const QSize &size, - const QMatrix4x4 &proj, const QMatrix4x4 &modelview, - float opacity) -{ - QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); - - m_program->bind(); - - QMatrix4x4 m = proj * modelview; - m_program->setUniformValue(m_matrixLoc, m); - m_program->setUniformValue(m_opacityLoc, opacity); - - m_buffer->bind(); - - if (size != m_prevSize) { - m_prevSize = size; - - QPointF p0(size.width() - 1, size.height() - 1); - QPointF p1(0, 0); - QPointF p2(0, size.height() - 1); - QPointF p3(size.width() - 1, 0); - - GLfloat vertices[6 * 4] = { - GLfloat(p0.x()), GLfloat(p0.y()), 1, 0, - GLfloat(p1.x()), GLfloat(p1.y()), 0, 1, - GLfloat(p2.x()), GLfloat(p2.y()), 0, 0, - - GLfloat(p0.x()), GLfloat(p0.y()), 1, 0, - GLfloat(p3.x()), GLfloat(p3.y()), 1, 1, - GLfloat(p1.x()), GLfloat(p1.y()), 0, 1, - }; - - m_buffer->write(0, vertices, sizeof(vertices)); - } - - m_program->enableAttributeArray(0); - m_program->enableAttributeArray(1); - f->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0); - f->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (const void *) (2 * sizeof(GLfloat))); - - f->glBindTexture(GL_TEXTURE_2D, textureId); - - f->glDrawArrays(GL_TRIANGLES, 0, 6); - - f->glBindTexture(GL_TEXTURE_2D, 0); - m_buffer->release(); - m_program->release(); -} - -QT_END_NAMESPACE diff --git a/src/imports/pathitem/qquickpathitemnvprrenderer_p.h b/src/imports/pathitem/qquickpathitemnvprrenderer_p.h deleted file mode 100644 index cfe1c7eab9..0000000000 --- a/src/imports/pathitem/qquickpathitemnvprrenderer_p.h +++ /dev/null @@ -1,237 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKPATHITEMNVPRRENDERER_P_H -#define QQUICKPATHITEMNVPRRENDERER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of a number of Qt sources files. This header file may change from -// version to version without notice, or even be removed. -// -// We mean it. -// - -#include "qquickpathitem_p_p.h" -#include "qquicknvprfunctions_p.h" -#include -#include -#include -#include - -#ifndef QT_NO_OPENGL - -QT_BEGIN_NAMESPACE - -class QQuickPathItemNvprRenderNode; -class QOpenGLFramebufferObject; -class QOpenGLBuffer; -class QOpenGLExtraFunctions; - -class QQuickPathItemNvprRenderer : public QQuickAbstractPathRenderer -{ -public: - enum Dirty { - DirtyPath = 0x01, - DirtyStyle = 0x02, - DirtyFillRule = 0x04, - DirtyDash = 0x08, - DirtyFillGradient = 0x10, - DirtyList = 0x20 - }; - - void beginSync(int totalCount) override; - void setPath(int index, const QQuickPath *path) override; - void setJSPath(int index, const QQuickPathItemPath &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 &dashPattern) override; - void setFillGradient(int index, QQuickPathGradient *gradient) override; - void endSync(bool async) override; - - void updateNode() override; - - void setNode(QQuickPathItemNvprRenderNode *node); - - struct NvprPath { - QVector cmd; - QVector coord; - QByteArray str; - }; - -private: - 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 dashPattern; - bool fillGradientActive; - QQuickPathItemGradientCache::GradientDesc fillGradient; - }; - - void convertPath(const QQuickPath *path, VisualPathGuiData *d); - void convertJSPath(const QQuickPathItemPath &path, VisualPathGuiData *d); - - QQuickPathItemNvprRenderNode *m_node = nullptr; - int m_accDirty = 0; - - QVector m_vp; -}; - -QDebug operator<<(QDebug debug, const QQuickPathItemNvprRenderer::NvprPath &path); - -class QQuickNvprMaterialManager -{ -public: - enum Material { - MatSolid, - MatLinearGradient, - - NMaterials - }; - - struct MaterialDesc { - GLuint ppl = 0; - GLuint prg = 0; - int uniLoc[4]; - }; - - void create(QQuickNvprFunctions *nvpr); - MaterialDesc *activateMaterial(Material m); - void releaseResources(); - -private: - QQuickNvprFunctions *m_nvpr; - MaterialDesc m_materials[NMaterials]; -}; - -class QQuickNvprBlitter -{ -public: - bool create(); - void destroy(); - bool isCreated() const { return m_program != nullptr; } - void texturedQuad(GLuint textureId, const QSize &size, - const QMatrix4x4 &proj, const QMatrix4x4 &modelview, - float opacity); - -private: - QOpenGLShaderProgram *m_program = nullptr; - QOpenGLBuffer *m_buffer = nullptr; - int m_matrixLoc; - int m_opacityLoc; - QSize m_prevSize; -}; - -class QQuickPathItemNvprRenderNode : public QSGRenderNode -{ -public: - ~QQuickPathItemNvprRenderNode(); - - void render(const RenderState *state) override; - void releaseResources() override; - StateFlags changedStates() const override; - RenderingFlags flags() const override; - - static bool isSupported(); - -private: - 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 dashPattern; - bool fillGradientActive; - QQuickPathItemGradientCache::GradientDesc fillGradient; - QOpenGLFramebufferObject *fallbackFbo = nullptr; - bool fallbackValid = false; - QSize fallbackSize; - QPointF fallbackTopLeft; - - bool hasFill() const { return !qFuzzyIsNull(fillColor.w()) || fillGradientActive; } - bool hasStroke() const { return strokeWidth >= 0.0f && !qFuzzyIsNull(strokeColor.w()); } - }; - - 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; - static QQuickNvprFunctions nvpr; - static QQuickNvprMaterialManager mtlmgr; - - QQuickNvprBlitter m_fallbackBlitter; - QOpenGLExtraFunctions *f = nullptr; - - QVector m_vp; - - friend class QQuickPathItemNvprRenderer; -}; - -QT_END_NAMESPACE - -#endif // QT_NO_OPENGL - -#endif // QQUICKPATHITEMNVPRRENDERER_P_H diff --git a/src/imports/pathitem/qquickpathitemsoftwarerenderer.cpp b/src/imports/pathitem/qquickpathitemsoftwarerenderer.cpp deleted file mode 100644 index b7aa93bf65..0000000000 --- a/src/imports/pathitem/qquickpathitemsoftwarerenderer.cpp +++ /dev/null @@ -1,277 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquickpathitemsoftwarerenderer_p.h" -#include - -QT_BEGIN_NAMESPACE - -void QQuickPathItemSoftwareRenderer::beginSync(int totalCount) -{ - if (m_vp.count() != totalCount) { - m_vp.resize(totalCount); - m_accDirty |= DirtyList; - } -} - -void QQuickPathItemSoftwareRenderer::setPath(int index, const QQuickPath *path) -{ - VisualPathGuiData &d(m_vp[index]); - d.path = path ? path->path() : QPainterPath(); - d.dirty |= DirtyPath; - m_accDirty |= DirtyPath; -} - -void QQuickPathItemSoftwareRenderer::setJSPath(int index, const QQuickPathItemPath &path) -{ - VisualPathGuiData &d(m_vp[index]); - d.path = path.toPainterPath(); - d.dirty |= DirtyPath; - m_accDirty |= DirtyPath; -} - -void QQuickPathItemSoftwareRenderer::setStrokeColor(int index, const QColor &color) -{ - VisualPathGuiData &d(m_vp[index]); - d.pen.setColor(color); - d.dirty |= DirtyPen; - m_accDirty |= DirtyPen; -} - -void QQuickPathItemSoftwareRenderer::setStrokeWidth(int index, qreal w) -{ - VisualPathGuiData &d(m_vp[index]); - d.strokeWidth = w; - if (w >= 0.0f) - d.pen.setWidthF(w); - d.dirty |= DirtyPen; - m_accDirty |= DirtyPen; -} - -void QQuickPathItemSoftwareRenderer::setFillColor(int index, const QColor &color) -{ - VisualPathGuiData &d(m_vp[index]); - d.fillColor = color; - d.brush.setColor(color); - d.dirty |= DirtyBrush; - m_accDirty |= DirtyBrush; -} - -void QQuickPathItemSoftwareRenderer::setFillRule(int index, QQuickVisualPath::FillRule fillRule) -{ - VisualPathGuiData &d(m_vp[index]); - d.fillRule = Qt::FillRule(fillRule); - d.dirty |= DirtyFillRule; - m_accDirty |= DirtyFillRule; -} - -void QQuickPathItemSoftwareRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) -{ - VisualPathGuiData &d(m_vp[index]); - d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); - d.pen.setMiterLimit(miterLimit); - d.dirty |= DirtyPen; - m_accDirty |= DirtyPen; -} - -void QQuickPathItemSoftwareRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) -{ - VisualPathGuiData &d(m_vp[index]); - d.pen.setCapStyle(Qt::PenCapStyle(capStyle)); - d.dirty |= DirtyPen; - m_accDirty |= DirtyPen; -} - -void QQuickPathItemSoftwareRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, - qreal dashOffset, const QVector &dashPattern) -{ - VisualPathGuiData &d(m_vp[index]); - switch (strokeStyle) { - case QQuickVisualPath::SolidLine: - d.pen.setStyle(Qt::SolidLine); - break; - case QQuickVisualPath::DashLine: - d.pen.setStyle(Qt::CustomDashLine); - d.pen.setDashPattern(dashPattern); - d.pen.setDashOffset(dashOffset); - break; - default: - break; - } - d.dirty |= DirtyPen; - m_accDirty |= DirtyPen; -} - -void QQuickPathItemSoftwareRenderer::setFillGradient(int index, QQuickPathGradient *gradient) -{ - VisualPathGuiData &d(m_vp[index]); - if (QQuickPathLinearGradient *linearGradient = qobject_cast(gradient)) { - QLinearGradient painterGradient(linearGradient->x1(), linearGradient->y1(), - linearGradient->x2(), linearGradient->y2()); - painterGradient.setStops(linearGradient->sortedGradientStops()); - switch (gradient->spread()) { - case QQuickPathGradient::PadSpread: - painterGradient.setSpread(QGradient::PadSpread); - break; - case QQuickPathGradient::RepeatSpread: - painterGradient.setSpread(QGradient::RepeatSpread); - break; - case QQuickPathGradient::ReflectSpread: - painterGradient.setSpread(QGradient::ReflectSpread); - break; - default: - break; - } - d.brush = QBrush(painterGradient); - } else { - d.brush = QBrush(d.fillColor); - } - d.dirty |= DirtyBrush; - m_accDirty |= DirtyBrush; -} - -void QQuickPathItemSoftwareRenderer::endSync(bool) -{ -} - -void QQuickPathItemSoftwareRenderer::setNode(QQuickPathItemSoftwareRenderNode *node) -{ - if (m_node != node) { - m_node = node; - m_accDirty |= DirtyList; - } -} - -void QQuickPathItemSoftwareRenderer::updateNode() -{ - if (!m_accDirty) - return; - - const int count = m_vp.count(); - const bool listChanged = m_accDirty & DirtyList; - if (listChanged) - m_node->m_vp.resize(count); - - m_node->m_boundingRect = QRectF(); - - for (int i = 0; i < count; ++i) { - VisualPathGuiData &src(m_vp[i]); - QQuickPathItemSoftwareRenderNode::VisualPathRenderData &dst(m_node->m_vp[i]); - - if (listChanged || (src.dirty & DirtyPath)) { - dst.path = src.path; - dst.path.setFillRule(src.fillRule); - } - - 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; - - src.dirty = 0; - - QRectF br = dst.path.boundingRect(); - const float sw = qMax(1.0f, dst.strokeWidth); - br.adjust(-sw, -sw, sw, sw); - m_node->m_boundingRect |= br; - } - - m_node->markDirty(QSGNode::DirtyMaterial); - m_accDirty = 0; -} - -QQuickPathItemSoftwareRenderNode::QQuickPathItemSoftwareRenderNode(QQuickPathItem *item) - : m_item(item) -{ -} - -QQuickPathItemSoftwareRenderNode::~QQuickPathItemSoftwareRenderNode() -{ - releaseResources(); -} - -void QQuickPathItemSoftwareRenderNode::releaseResources() -{ -} - -void QQuickPathItemSoftwareRenderNode::render(const RenderState *state) -{ - if (m_vp.isEmpty()) - return; - - QSGRendererInterface *rif = m_item->window()->rendererInterface(); - QPainter *p = static_cast(rif->getResource(m_item->window(), QSGRendererInterface::PainterResource)); - Q_ASSERT(p); - - const QRegion *clipRegion = state->clipRegion(); - if (clipRegion && !clipRegion->isEmpty()) - p->setClipRegion(*clipRegion, Qt::ReplaceClip); // must be done before setTransform - - p->setTransform(matrix()->toTransform()); - p->setOpacity(inheritedOpacity()); - - 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 -{ - return 0; -} - -QSGRenderNode::RenderingFlags QQuickPathItemSoftwareRenderNode::flags() const -{ - return BoundedRectRendering; // avoid fullscreen updates by saying we won't draw outside rect() -} - -QRectF QQuickPathItemSoftwareRenderNode::rect() const -{ - return m_boundingRect; -} - -QT_END_NAMESPACE diff --git a/src/imports/pathitem/qquickpathitemsoftwarerenderer_p.h b/src/imports/pathitem/qquickpathitemsoftwarerenderer_p.h deleted file mode 100644 index e76590bdfe..0000000000 --- a/src/imports/pathitem/qquickpathitemsoftwarerenderer_p.h +++ /dev/null @@ -1,136 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKPATHITEMSOFTWARERENDERER_P_H -#define QQUICKPATHITEMSOFTWARERENDERER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of a number of Qt sources files. This header file may change from -// version to version without notice, or even be removed. -// -// We mean it. -// - -#include "qquickpathitem_p_p.h" -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QQuickPathItemSoftwareRenderNode; - -class QQuickPathItemSoftwareRenderer : public QQuickAbstractPathRenderer -{ -public: - enum Dirty { - DirtyPath = 0x01, - DirtyPen = 0x02, - DirtyFillRule = 0x04, - DirtyBrush = 0x08, - DirtyList = 0x10 - }; - - void beginSync(int totalCount) override; - void setPath(int index, const QQuickPath *path) override; - void setJSPath(int index, const QQuickPathItemPath &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 &dashPattern) override; - void setFillGradient(int index, QQuickPathGradient *gradient) override; - void endSync(bool async) override; - - void updateNode() override; - - void setNode(QQuickPathItemSoftwareRenderNode *node); - -private: - QQuickPathItemSoftwareRenderNode *m_node = nullptr; - int m_accDirty = 0; - struct VisualPathGuiData { - int dirty = 0; - QPainterPath path; - QPen pen; - float strokeWidth; - QColor fillColor; - QBrush brush; - Qt::FillRule fillRule; - }; - QVector m_vp; -}; - -class QQuickPathItemSoftwareRenderNode : public QSGRenderNode -{ -public: - QQuickPathItemSoftwareRenderNode(QQuickPathItem *item); - ~QQuickPathItemSoftwareRenderNode(); - - void render(const RenderState *state) override; - void releaseResources() override; - StateFlags changedStates() const override; - RenderingFlags flags() const override; - QRectF rect() const override; - -private: - QQuickPathItem *m_item; - - struct VisualPathRenderData { - QPainterPath path; - QPen pen; - float strokeWidth; - QBrush brush; - }; - QVector m_vp; - QRectF m_boundingRect; - - friend class QQuickPathItemSoftwareRenderer; -}; - -QT_END_NAMESPACE - -#endif // QQUICKPATHITEMSOFTWARERENDERER_P_H diff --git a/src/imports/shapes/plugin.cpp b/src/imports/shapes/plugin.cpp new file mode 100644 index 0000000000..2f2f8c74d3 --- /dev/null +++ b/src/imports/shapes/plugin.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "qquickshape_p.h" + +static void initResources() +{ +#ifdef QT_STATIC + Q_INIT_RESOURCE(qmake_QtQuick_Shapes); +#endif +} + +QT_BEGIN_NAMESPACE + +class QmlShapesPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + QmlShapesPlugin(QObject *parent = 0) : QQmlExtensionPlugin(parent) { initResources(); } + void registerTypes(const char *uri) Q_DECL_OVERRIDE + { + Q_ASSERT(QByteArray(uri) == QByteArray("QtQuick.Shapes")); + qmlRegisterType(uri, 1, 0, "Shape"); + qmlRegisterType(uri, 1, 0, "ShapePath"); + qmlRegisterType(uri, 1, 0, "ShapeGradientStop"); + qmlRegisterUncreatableType(uri, 1, 0, "ShapeGradient", QQuickShapeGradient::tr("ShapeGradient is an abstract base class")); + qmlRegisterType(uri, 1, 0, "ShapeLinearGradient"); + } +}; + +QT_END_NAMESPACE + +#include "plugin.moc" diff --git a/src/imports/shapes/plugins.qmltypes b/src/imports/shapes/plugins.qmltypes new file mode 100644 index 0000000000..28d8dd1f12 --- /dev/null +++ b/src/imports/shapes/plugins.qmltypes @@ -0,0 +1,292 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by: +// 'qmlplugindump -nonrelocatable -noforceqtquick QtQuick.Shapes 1.0' + +Module { + dependencies: [] + Component { + name: "QQuickItem" + defaultProperty: "data" + prototype: "QObject" + Enum { + name: "TransformOrigin" + values: { + "TopLeft": 0, + "Top": 1, + "TopRight": 2, + "Left": 3, + "Center": 4, + "Right": 5, + "BottomLeft": 6, + "Bottom": 7, + "BottomRight": 8 + } + } + Property { name: "parent"; type: "QQuickItem"; isPointer: true } + Property { name: "data"; type: "QObject"; isList: true; isReadonly: true } + Property { name: "resources"; type: "QObject"; isList: true; isReadonly: true } + Property { name: "children"; type: "QQuickItem"; isList: true; isReadonly: true } + Property { name: "x"; type: "double" } + Property { name: "y"; type: "double" } + Property { name: "z"; type: "double" } + Property { name: "width"; type: "double" } + Property { name: "height"; type: "double" } + Property { name: "opacity"; type: "double" } + Property { name: "enabled"; type: "bool" } + Property { name: "visible"; type: "bool" } + Property { name: "visibleChildren"; type: "QQuickItem"; isList: true; isReadonly: true } + Property { name: "states"; type: "QQuickState"; isList: true; isReadonly: true } + Property { name: "transitions"; type: "QQuickTransition"; isList: true; isReadonly: true } + Property { name: "state"; type: "string" } + Property { name: "childrenRect"; type: "QRectF"; isReadonly: true } + Property { name: "anchors"; type: "QQuickAnchors"; isReadonly: true; isPointer: true } + Property { name: "left"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "right"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "horizontalCenter"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "top"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "bottom"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "verticalCenter"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "baseline"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "baselineOffset"; type: "double" } + Property { name: "clip"; type: "bool" } + Property { name: "focus"; type: "bool" } + Property { name: "activeFocus"; type: "bool"; isReadonly: true } + Property { name: "activeFocusOnTab"; revision: 1; type: "bool" } + Property { name: "rotation"; type: "double" } + Property { name: "scale"; type: "double" } + Property { name: "transformOrigin"; type: "TransformOrigin" } + Property { name: "transformOriginPoint"; type: "QPointF"; isReadonly: true } + Property { name: "transform"; type: "QQuickTransform"; isList: true; isReadonly: true } + Property { name: "smooth"; type: "bool" } + Property { name: "antialiasing"; type: "bool" } + Property { name: "implicitWidth"; type: "double" } + Property { name: "implicitHeight"; type: "double" } + Property { name: "layer"; type: "QQuickItemLayer"; isReadonly: true; isPointer: true } + Signal { + name: "childrenRectChanged" + Parameter { type: "QRectF" } + } + Signal { + name: "baselineOffsetChanged" + Parameter { type: "double" } + } + Signal { + name: "stateChanged" + Parameter { type: "string" } + } + Signal { + name: "focusChanged" + Parameter { type: "bool" } + } + Signal { + name: "activeFocusChanged" + Parameter { type: "bool" } + } + Signal { + name: "activeFocusOnTabChanged" + revision: 1 + Parameter { type: "bool" } + } + Signal { + name: "parentChanged" + Parameter { type: "QQuickItem"; isPointer: true } + } + Signal { + name: "transformOriginChanged" + Parameter { type: "TransformOrigin" } + } + Signal { + name: "smoothChanged" + Parameter { type: "bool" } + } + Signal { + name: "antialiasingChanged" + Parameter { type: "bool" } + } + Signal { + name: "clipChanged" + Parameter { type: "bool" } + } + Signal { + name: "windowChanged" + revision: 1 + Parameter { name: "window"; type: "QQuickWindow"; isPointer: true } + } + Method { name: "update" } + Method { + name: "grabToImage" + revision: 2 + type: "bool" + Parameter { name: "callback"; type: "QJSValue" } + Parameter { name: "targetSize"; type: "QSize" } + } + Method { + name: "grabToImage" + revision: 2 + type: "bool" + Parameter { name: "callback"; type: "QJSValue" } + } + Method { + name: "contains" + type: "bool" + Parameter { name: "point"; type: "QPointF" } + } + Method { + name: "mapFromItem" + Parameter { type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "mapToItem" + Parameter { type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "mapFromGlobal" + revision: 7 + Parameter { type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "mapToGlobal" + revision: 7 + Parameter { type: "QQmlV4Function"; isPointer: true } + } + Method { name: "forceActiveFocus" } + Method { + name: "forceActiveFocus" + Parameter { name: "reason"; type: "Qt::FocusReason" } + } + Method { + name: "nextItemInFocusChain" + revision: 1 + type: "QQuickItem*" + Parameter { name: "forward"; type: "bool" } + } + Method { name: "nextItemInFocusChain"; revision: 1; type: "QQuickItem*" } + Method { + name: "childAt" + type: "QQuickItem*" + Parameter { name: "x"; type: "double" } + Parameter { name: "y"; type: "double" } + } + } + Component { + name: "QQuickShapeGradient" + defaultProperty: "stops" + prototype: "QObject" + exports: ["QtQuick.Shapes/ShapeGradient 1.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + Enum { + name: "SpreadMode" + values: { + "PadSpread": 0, + "RepeatSpread": 1, + "ReflectSpread": 2 + } + } + Property { name: "stops"; type: "QObject"; isList: true; isReadonly: true } + Property { name: "spread"; type: "SpreadMode" } + Signal { name: "updated" } + } + Component { + name: "QQuickShapeGradientStop" + prototype: "QObject" + exports: ["QtQuick.Shapes/ShapeGradientStop 1.0"] + exportMetaObjectRevisions: [0] + Property { name: "position"; type: "double" } + Property { name: "color"; type: "QColor" } + } + Component { + name: "QQuickShape" + defaultProperty: "elements" + prototype: "QQuickItem" + exports: ["QtQuick.Shapes/Shape 1.0"] + exportMetaObjectRevisions: [0] + Enum { + name: "RendererType" + values: { + "UnknownRenderer": 0, + "GeometryRenderer": 1, + "NvprRenderer": 2, + "SoftwareRenderer": 3 + } + } + Enum { + name: "Status" + values: { + "Null": 0, + "Ready": 1, + "Processing": 2 + } + } + Property { name: "renderer"; type: "RendererType"; isReadonly: true } + Property { name: "asynchronous"; type: "bool" } + Property { name: "enableVendorExtensions"; type: "bool" } + Property { name: "status"; type: "Status"; isReadonly: true } + Property { name: "elements"; type: "QQuickShapePath"; isList: true; isReadonly: true } + } + Component { + name: "QQuickShapeLinearGradient" + defaultProperty: "stops" + prototype: "QQuickShapeGradient" + exports: ["QtQuick.Shapes/ShapeLinearGradient 1.0"] + exportMetaObjectRevisions: [0] + Property { name: "x1"; type: "double" } + Property { name: "y1"; type: "double" } + Property { name: "x2"; type: "double" } + Property { name: "y2"; type: "double" } + } + Component { + name: "QQuickShapePath" + defaultProperty: "path" + prototype: "QObject" + exports: ["QtQuick.Shapes/ShapePath 1.0"] + exportMetaObjectRevisions: [0] + Enum { + name: "FillRule" + values: { + "OddEvenFill": 0, + "WindingFill": 1 + } + } + Enum { + name: "JoinStyle" + values: { + "MiterJoin": 0, + "BevelJoin": 64, + "RoundJoin": 128 + } + } + Enum { + name: "CapStyle" + values: { + "FlatCap": 0, + "SquareCap": 16, + "RoundCap": 32 + } + } + Enum { + name: "StrokeStyle" + values: { + "SolidLine": 1, + "DashLine": 2 + } + } + Property { name: "path"; type: "QQuickPath"; isPointer: true } + Property { name: "strokeColor"; type: "QColor" } + Property { name: "strokeWidth"; type: "double" } + Property { name: "fillColor"; type: "QColor" } + Property { name: "fillRule"; type: "FillRule" } + Property { name: "joinStyle"; type: "JoinStyle" } + Property { name: "miterLimit"; type: "int" } + Property { name: "capStyle"; type: "CapStyle" } + Property { name: "strokeStyle"; type: "StrokeStyle" } + Property { name: "dashOffset"; type: "double" } + Property { name: "dashPattern"; type: "QVector" } + Property { name: "fillGradient"; type: "QQuickShapeGradient"; isPointer: true } + Signal { name: "changed" } + } +} diff --git a/src/imports/shapes/qmldir b/src/imports/shapes/qmldir new file mode 100644 index 0000000000..306ad1ecd7 --- /dev/null +++ b/src/imports/shapes/qmldir @@ -0,0 +1,4 @@ +module QtQuick.Shapes +plugin qmlshapesplugin +classname QmlShapesPlugin +typeinfo plugins.qmltypes diff --git a/src/imports/shapes/qquicknvprfunctions.cpp b/src/imports/shapes/qquicknvprfunctions.cpp new file mode 100644 index 0000000000..e9b93cf4a1 --- /dev/null +++ b/src/imports/shapes/qquicknvprfunctions.cpp @@ -0,0 +1,277 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicknvprfunctions_p.h" + +#ifndef QT_NO_OPENGL + +#include +#include +#include +#include "qquicknvprfunctions_p_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QQuickNvprFunctions + + \brief Function resolvers and other helpers for GL_NV_path_rendering + for both desktop (GL 4.3+) and mobile/embedded (GLES 3.1+) in a manner + that does not distract builds that do not have NVPR support either at + compile or run time. + + \internal + */ + +QQuickNvprFunctions::QQuickNvprFunctions() + : d(new QQuickNvprFunctionsPrivate(this)) +{ +} + +QQuickNvprFunctions::~QQuickNvprFunctions() +{ + delete d; +} + +/*! + \return a recommended QSurfaceFormat suitable for GL_NV_path_rendering on top + of OpenGL 4.3 or OpenGL ES 3.1. + */ +QSurfaceFormat QQuickNvprFunctions::format() +{ + QSurfaceFormat fmt; + fmt.setDepthBufferSize(24); + fmt.setStencilBufferSize(8); + if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { + fmt.setVersion(4, 3); + fmt.setProfile(QSurfaceFormat::CompatibilityProfile); + } else if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { + fmt.setVersion(3, 1); + } + return fmt; +} + +/*! + \return true if GL_NV_path_rendering is supported with the current OpenGL + context. + + When there is no current context, a temporary dummy one will be created and + made current. + */ +bool QQuickNvprFunctions::isSupported() +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + QScopedPointer tempContext; + QScopedPointer tempSurface; + if (!ctx) { + tempContext.reset(new QOpenGLContext); + if (!tempContext->create()) + return false; + ctx = tempContext.data(); + tempSurface.reset(new QOffscreenSurface); + tempSurface->setFormat(ctx->format()); + tempSurface->create(); + if (!ctx->makeCurrent(tempSurface.data())) + return false; + } + + if (!ctx->hasExtension(QByteArrayLiteral("GL_NV_path_rendering"))) + return false; + + // Do not check for DSA as the string may not be exposed on ES + // drivers, yet the functions we need are resolvable. +#if 0 + if (!ctx->hasExtension(QByteArrayLiteral("GL_EXT_direct_state_access"))) { + qWarning("QtQuickPath/NVPR: GL_EXT_direct_state_access not supported"); + return false; + } +#endif + + return true; +} + +/*! + Initializes using the current OpenGL context. + + \return true when GL_NV_path_rendering is supported and initialization was + successful. + */ +bool QQuickNvprFunctions::create() +{ + return isSupported() && d->resolve(); +} + +/*! + Creates a program pipeline consisting of a separable fragment shader program. + + This is essential for using NVPR with OpenGL ES 3.1+ since normal, + GLES2-style programs would not work without a vertex shader. + + \note \a fragmentShaderSource should be a \c{version 310 es} shader since + this works both on desktop and embedded NVIDIA drivers, thus avoiding the + need to fight GLSL and GLSL ES differences. + + The pipeline object is stored into \a pipeline, the fragment shader program + into \a program. + + Use QOpenGLExtraFunctions to set uniforms, bind the pipeline, etc. + + \return \c false on failure in which case the error log is printed on the + debug output. \c true on success. + */ +bool QQuickNvprFunctions::createFragmentOnlyPipeline(const char *fragmentShaderSource, GLuint *pipeline, GLuint *program) +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (!ctx) + return false; + + QOpenGLExtraFunctions *f = ctx->extraFunctions(); + *program = f->glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &fragmentShaderSource); + GLint status = 0; + f->glGetProgramiv(*program, GL_LINK_STATUS, &status); + if (!status) { + GLint len = 0; + f->glGetProgramiv(*program, GL_INFO_LOG_LENGTH, &len); + if (len) { + QByteArray s; + s.resize(len); + f->glGetProgramInfoLog(*program, s.count(), nullptr, s.data()); + qWarning("Failed to create separable shader program:\n%s", s.constData()); + } + return false; + } + + f->glGenProgramPipelines(1, pipeline); + f->glUseProgramStages(*pipeline, GL_FRAGMENT_SHADER_BIT, *program); + f->glActiveShaderProgram(*pipeline, *program); + + f->glValidateProgramPipeline(*pipeline); + status = 0; + f->glGetProgramPipelineiv(*pipeline, GL_VALIDATE_STATUS, &status); + if (!status) { + GLint len = 0; + f->glGetProgramPipelineiv(*pipeline, GL_INFO_LOG_LENGTH, &len); + if (len) { + QByteArray s; + s.resize(len); + f->glGetProgramPipelineInfoLog(*pipeline, s.count(), nullptr, s.data()); + qWarning("Program pipeline validation failed:\n%s", s.constData()); + } + return false; + } + + return true; +} + +#define PROC(type, name) reinterpret_cast(ctx->getProcAddress(#name)) + +bool QQuickNvprFunctionsPrivate::resolve() +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + + q->genPaths = PROC(PFNGLGENPATHSNVPROC, glGenPathsNV); + q->deletePaths = PROC(PFNGLDELETEPATHSNVPROC, glDeletePathsNV); + q->isPath = PROC(PFNGLISPATHNVPROC, glIsPathNV); + q->pathCommands = PROC(PFNGLPATHCOMMANDSNVPROC, glPathCommandsNV); + q->pathCoords = PROC(PFNGLPATHCOORDSNVPROC, glPathCoordsNV); + q->pathSubCommands = PROC(PFNGLPATHSUBCOMMANDSNVPROC, glPathSubCommandsNV); + q->pathSubCoords = PROC(PFNGLPATHSUBCOORDSNVPROC, glPathSubCoordsNV); + q->pathString = PROC(PFNGLPATHSTRINGNVPROC, glPathStringNV); + q->pathGlyphs = PROC(PFNGLPATHGLYPHSNVPROC, glPathGlyphsNV); + q->pathGlyphRange = PROC(PFNGLPATHGLYPHRANGENVPROC, glPathGlyphRangeNV); + q->weightPaths = PROC(PFNGLWEIGHTPATHSNVPROC, glWeightPathsNV); + q->copyPath = PROC(PFNGLCOPYPATHNVPROC, glCopyPathNV); + q->interpolatePaths = PROC(PFNGLINTERPOLATEPATHSNVPROC, glInterpolatePathsNV); + q->transformPath = PROC(PFNGLTRANSFORMPATHNVPROC, glTransformPathNV); + q->pathParameteriv = PROC(PFNGLPATHPARAMETERIVNVPROC, glPathParameterivNV); + q->pathParameteri = PROC(PFNGLPATHPARAMETERINVPROC, glPathParameteriNV); + q->pathParameterfv = PROC(PFNGLPATHPARAMETERFVNVPROC, glPathParameterfvNV); + q->pathParameterf = PROC(PFNGLPATHPARAMETERFNVPROC, glPathParameterfNV); + q->pathDashArray = PROC(PFNGLPATHDASHARRAYNVPROC, glPathDashArrayNV); + q->pathStencilFunc = PROC(PFNGLPATHSTENCILFUNCNVPROC, glPathStencilFuncNV); + q->pathStencilDepthOffset = PROC(PFNGLPATHSTENCILDEPTHOFFSETNVPROC, glPathStencilDepthOffsetNV); + q->stencilFillPath = PROC(PFNGLSTENCILFILLPATHNVPROC, glStencilFillPathNV); + q->stencilStrokePath = PROC(PFNGLSTENCILSTROKEPATHNVPROC, glStencilStrokePathNV); + q->stencilFillPathInstanced = PROC(PFNGLSTENCILFILLPATHINSTANCEDNVPROC, glStencilFillPathInstancedNV); + q->stencilStrokePathInstanced = PROC(PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC, glStencilStrokePathInstancedNV); + q->pathCoverDepthFunc = PROC(PFNGLPATHCOVERDEPTHFUNCNVPROC, glPathCoverDepthFuncNV); + q->coverFillPath = PROC(PFNGLCOVERFILLPATHNVPROC, glCoverFillPathNV); + q->coverStrokePath = PROC(PFNGLCOVERSTROKEPATHNVPROC, glCoverStrokePathNV); + q->coverFillPathInstanced = PROC(PFNGLCOVERFILLPATHINSTANCEDNVPROC, glCoverFillPathInstancedNV); + q->coverStrokePathInstanced = PROC(PFNGLCOVERSTROKEPATHINSTANCEDNVPROC, glCoverStrokePathInstancedNV); + q->getPathParameteriv = PROC(PFNGLGETPATHPARAMETERIVNVPROC, glGetPathParameterivNV); + q->getPathParameterfv = PROC(PFNGLGETPATHPARAMETERFVNVPROC, glGetPathParameterfvNV); + q->getPathCommands = PROC(PFNGLGETPATHCOMMANDSNVPROC, glGetPathCommandsNV); + q->getPathCoords = PROC(PFNGLGETPATHCOORDSNVPROC, glGetPathCoordsNV); + q->getPathDashArray = PROC(PFNGLGETPATHDASHARRAYNVPROC, glGetPathDashArrayNV); + q->getPathMetrics = PROC(PFNGLGETPATHMETRICSNVPROC, glGetPathMetricsNV); + q->getPathMetricRange = PROC(PFNGLGETPATHMETRICRANGENVPROC, glGetPathMetricRangeNV); + q->getPathSpacing = PROC(PFNGLGETPATHSPACINGNVPROC, glGetPathSpacingNV); + q->isPointInFillPath = PROC(PFNGLISPOINTINFILLPATHNVPROC, glIsPointInFillPathNV); + q->isPointInStrokePath = PROC(PFNGLISPOINTINSTROKEPATHNVPROC, glIsPointInStrokePathNV); + q->getPathLength = PROC(PFNGLGETPATHLENGTHNVPROC, glGetPathLengthNV); + q->getPointAlongPath = PROC(PFNGLPOINTALONGPATHNVPROC, glPointAlongPathNV); + q->matrixLoad3x2f = PROC(PFNGLMATRIXLOAD3X2FNVPROC, glMatrixLoad3x2fNV); + q->matrixLoad3x3f = PROC(PFNGLMATRIXLOAD3X3FNVPROC, glMatrixLoad3x3fNV); + q->matrixLoadTranspose3x3f = PROC(PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC, glMatrixLoadTranspose3x3fNV); + q->matrixMult3x2f = PROC(PFNGLMATRIXMULT3X2FNVPROC, glMatrixMult3x2fNV); + q->matrixMult3x3f = PROC(PFNGLMATRIXMULT3X3FNVPROC, glMatrixMult3x3fNV); + q->matrixMultTranspose3x3f = PROC(PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC, glMatrixMultTranspose3x3fNV); + q->stencilThenCoverFillPath = PROC(PFNGLSTENCILTHENCOVERFILLPATHNVPROC, glStencilThenCoverFillPathNV); + q->stencilThenCoverStrokePath = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC, glStencilThenCoverStrokePathNV); + q->stencilThenCoverFillPathInstanced = PROC(PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC, glStencilThenCoverFillPathInstancedNV); + q->stencilThenCoverStrokePathInstanced = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC, glStencilThenCoverStrokePathInstancedNV); + q->pathGlyphIndexRange = PROC(PFNGLPATHGLYPHINDEXRANGENVPROC, glPathGlyphIndexRangeNV); + q->pathGlyphIndexArray = PROC(PFNGLPATHGLYPHINDEXARRAYNVPROC, glPathGlyphIndexArrayNV); + q->pathMemoryGlyphIndexArray = PROC(PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC, glPathMemoryGlyphIndexArrayNV); + q->programPathFragmentInputGen = PROC(PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC, glProgramPathFragmentInputGenNV); + q->getProgramResourcefv = PROC(PFNGLGETPROGRAMRESOURCEFVNVPROC, glGetProgramResourcefvNV); + + q->matrixLoadf = PROC(PFNGLMATRIXLOADFEXTPROC, glMatrixLoadfEXT); + q->matrixLoadIdentity = PROC(PFNGLMATRIXLOADIDENTITYEXTPROC, glMatrixLoadIdentityEXT); + + return q->genPaths != nullptr // base path rendering ext + && q->programPathFragmentInputGen != nullptr // updated path rendering ext + && q->matrixLoadf != nullptr // direct state access ext + && q->matrixLoadIdentity != nullptr; +} + +QT_END_NAMESPACE + +#endif // QT_NO_OPENGL diff --git a/src/imports/shapes/qquicknvprfunctions_p.h b/src/imports/shapes/qquicknvprfunctions_p.h new file mode 100644 index 0000000000..dd45dd7daa --- /dev/null +++ b/src/imports/shapes/qquicknvprfunctions_p.h @@ -0,0 +1,400 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKNVPRFUNCTIONS_P_H +#define QQUICKNVPRFUNCTIONS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#ifndef QT_NO_OPENGL + +QT_BEGIN_NAMESPACE + +// note: fixed pipeline specific functions are removed - modern ES ext +// headers have all this, but not the fixed stuff + +#ifndef GL_NV_path_rendering +#define GL_PATH_FORMAT_SVG_NV 0x9070 +#define GL_PATH_FORMAT_PS_NV 0x9071 +#define GL_STANDARD_FONT_NAME_NV 0x9072 +#define GL_SYSTEM_FONT_NAME_NV 0x9073 +#define GL_FILE_NAME_NV 0x9074 +#define GL_PATH_STROKE_WIDTH_NV 0x9075 +#define GL_PATH_END_CAPS_NV 0x9076 +#define GL_PATH_INITIAL_END_CAP_NV 0x9077 +#define GL_PATH_TERMINAL_END_CAP_NV 0x9078 +#define GL_PATH_JOIN_STYLE_NV 0x9079 +#define GL_PATH_MITER_LIMIT_NV 0x907A +#define GL_PATH_DASH_CAPS_NV 0x907B +#define GL_PATH_INITIAL_DASH_CAP_NV 0x907C +#define GL_PATH_TERMINAL_DASH_CAP_NV 0x907D +#define GL_PATH_DASH_OFFSET_NV 0x907E +#define GL_PATH_CLIENT_LENGTH_NV 0x907F +#define GL_PATH_FILL_MODE_NV 0x9080 +#define GL_PATH_FILL_MASK_NV 0x9081 +#define GL_PATH_FILL_COVER_MODE_NV 0x9082 +#define GL_PATH_STROKE_COVER_MODE_NV 0x9083 +#define GL_PATH_STROKE_MASK_NV 0x9084 +#define GL_COUNT_UP_NV 0x9088 +#define GL_COUNT_DOWN_NV 0x9089 +#define GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A +#define GL_CONVEX_HULL_NV 0x908B +#define GL_BOUNDING_BOX_NV 0x908D +#define GL_TRANSLATE_X_NV 0x908E +#define GL_TRANSLATE_Y_NV 0x908F +#define GL_TRANSLATE_2D_NV 0x9090 +#define GL_TRANSLATE_3D_NV 0x9091 +#define GL_AFFINE_2D_NV 0x9092 +#define GL_AFFINE_3D_NV 0x9094 +#define GL_TRANSPOSE_AFFINE_2D_NV 0x9096 +#define GL_TRANSPOSE_AFFINE_3D_NV 0x9098 +#define GL_UTF8_NV 0x909A +#define GL_UTF16_NV 0x909B +#define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C +#define GL_PATH_COMMAND_COUNT_NV 0x909D +#define GL_PATH_COORD_COUNT_NV 0x909E +#define GL_PATH_DASH_ARRAY_COUNT_NV 0x909F +#define GL_PATH_COMPUTED_LENGTH_NV 0x90A0 +#define GL_PATH_FILL_BOUNDING_BOX_NV 0x90A1 +#define GL_PATH_STROKE_BOUNDING_BOX_NV 0x90A2 +#define GL_SQUARE_NV 0x90A3 +#define GL_ROUND_NV 0x90A4 +#define GL_TRIANGULAR_NV 0x90A5 +#define GL_BEVEL_NV 0x90A6 +#define GL_MITER_REVERT_NV 0x90A7 +#define GL_MITER_TRUNCATE_NV 0x90A8 +#define GL_SKIP_MISSING_GLYPH_NV 0x90A9 +#define GL_USE_MISSING_GLYPH_NV 0x90AA +#define GL_PATH_ERROR_POSITION_NV 0x90AB +#define GL_PATH_FOG_GEN_MODE_NV 0x90AC +#define GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD +#define GL_ADJACENT_PAIRS_NV 0x90AE +#define GL_FIRST_TO_REST_NV 0x90AF +#define GL_PATH_GEN_MODE_NV 0x90B0 +#define GL_PATH_GEN_COEFF_NV 0x90B1 +#define GL_PATH_GEN_COLOR_FORMAT_NV 0x90B2 +#define GL_PATH_GEN_COMPONENTS_NV 0x90B3 +#define GL_PATH_STENCIL_FUNC_NV 0x90B7 +#define GL_PATH_STENCIL_REF_NV 0x90B8 +#define GL_PATH_STENCIL_VALUE_MASK_NV 0x90B9 +#define GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD +#define GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE +#define GL_PATH_COVER_DEPTH_FUNC_NV 0x90BF +#define GL_PATH_DASH_OFFSET_RESET_NV 0x90B4 +#define GL_MOVE_TO_RESETS_NV 0x90B5 +#define GL_MOVE_TO_CONTINUES_NV 0x90B6 +#define GL_CLOSE_PATH_NV 0x00 +#define GL_MOVE_TO_NV 0x02 +#define GL_RELATIVE_MOVE_TO_NV 0x03 +#define GL_LINE_TO_NV 0x04 +#define GL_RELATIVE_LINE_TO_NV 0x05 +#define GL_HORIZONTAL_LINE_TO_NV 0x06 +#define GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07 +#define GL_VERTICAL_LINE_TO_NV 0x08 +#define GL_RELATIVE_VERTICAL_LINE_TO_NV 0x09 +#define GL_QUADRATIC_CURVE_TO_NV 0x0A +#define GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B +#define GL_CUBIC_CURVE_TO_NV 0x0C +#define GL_RELATIVE_CUBIC_CURVE_TO_NV 0x0D +#define GL_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E +#define GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F +#define GL_SMOOTH_CUBIC_CURVE_TO_NV 0x10 +#define GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11 +#define GL_SMALL_CCW_ARC_TO_NV 0x12 +#define GL_RELATIVE_SMALL_CCW_ARC_TO_NV 0x13 +#define GL_SMALL_CW_ARC_TO_NV 0x14 +#define GL_RELATIVE_SMALL_CW_ARC_TO_NV 0x15 +#define GL_LARGE_CCW_ARC_TO_NV 0x16 +#define GL_RELATIVE_LARGE_CCW_ARC_TO_NV 0x17 +#define GL_LARGE_CW_ARC_TO_NV 0x18 +#define GL_RELATIVE_LARGE_CW_ARC_TO_NV 0x19 +#define GL_RESTART_PATH_NV 0xF0 +#define GL_DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2 +#define GL_DUP_LAST_CUBIC_CURVE_TO_NV 0xF4 +#define GL_RECT_NV 0xF6 +#define GL_CIRCULAR_CCW_ARC_TO_NV 0xF8 +#define GL_CIRCULAR_CW_ARC_TO_NV 0xFA +#define GL_CIRCULAR_TANGENT_ARC_TO_NV 0xFC +#define GL_ARC_TO_NV 0xFE +#define GL_RELATIVE_ARC_TO_NV 0xFF +#define GL_BOLD_BIT_NV 0x01 +#define GL_ITALIC_BIT_NV 0x02 +#define GL_GLYPH_WIDTH_BIT_NV 0x01 +#define GL_GLYPH_HEIGHT_BIT_NV 0x02 +#define GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04 +#define GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08 +#define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10 +#define GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20 +#define GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40 +#define GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80 +#define GL_GLYPH_HAS_KERNING_BIT_NV 0x100 +#define GL_FONT_X_MIN_BOUNDS_BIT_NV 0x00010000 +#define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000 +#define GL_FONT_X_MAX_BOUNDS_BIT_NV 0x00040000 +#define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000 +#define GL_FONT_UNITS_PER_EM_BIT_NV 0x00100000 +#define GL_FONT_ASCENDER_BIT_NV 0x00200000 +#define GL_FONT_DESCENDER_BIT_NV 0x00400000 +#define GL_FONT_HEIGHT_BIT_NV 0x00800000 +#define GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000 +#define GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000 +#define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000 +#define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000 +#define GL_FONT_HAS_KERNING_BIT_NV 0x10000000 +#define GL_PRIMARY_COLOR_NV 0x852C +#define GL_SECONDARY_COLOR_NV 0x852D +#define GL_ROUNDED_RECT_NV 0xE8 +#define GL_RELATIVE_ROUNDED_RECT_NV 0xE9 +#define GL_ROUNDED_RECT2_NV 0xEA +#define GL_RELATIVE_ROUNDED_RECT2_NV 0xEB +#define GL_ROUNDED_RECT4_NV 0xEC +#define GL_RELATIVE_ROUNDED_RECT4_NV 0xED +#define GL_ROUNDED_RECT8_NV 0xEE +#define GL_RELATIVE_ROUNDED_RECT8_NV 0xEF +#define GL_RELATIVE_RECT_NV 0xF7 +#define GL_FONT_GLYPHS_AVAILABLE_NV 0x9368 +#define GL_FONT_TARGET_UNAVAILABLE_NV 0x9369 +#define GL_FONT_UNAVAILABLE_NV 0x936A +#define GL_FONT_UNINTELLIGIBLE_NV 0x936B +#define GL_CONIC_CURVE_TO_NV 0x1A +#define GL_RELATIVE_CONIC_CURVE_TO_NV 0x1B +#define GL_FONT_NUM_GLYPH_INDICES_BIT_NV 0x20000000 +#define GL_STANDARD_FONT_FORMAT_NV 0x936C +#define GL_2_BYTES_NV 0x1407 +#define GL_3_BYTES_NV 0x1408 +#define GL_4_BYTES_NV 0x1409 +#define GL_EYE_LINEAR_NV 0x2400 +#define GL_OBJECT_LINEAR_NV 0x2401 +#define GL_CONSTANT_NV 0x8576 +#define GL_PATH_PROJECTION_NV 0x1701 +#define GL_PATH_MODELVIEW_NV 0x1700 +#define GL_PATH_MODELVIEW_STACK_DEPTH_NV 0x0BA3 +#define GL_PATH_MODELVIEW_MATRIX_NV 0x0BA6 +#define GL_PATH_MAX_MODELVIEW_STACK_DEPTH_NV 0x0D36 +#define GL_PATH_TRANSPOSE_MODELVIEW_MATRIX_NV 0x84E3 +#define GL_PATH_PROJECTION_STACK_DEPTH_NV 0x0BA4 +#define GL_PATH_PROJECTION_MATRIX_NV 0x0BA7 +#define GL_PATH_MAX_PROJECTION_STACK_DEPTH_NV 0x0D38 +#define GL_PATH_TRANSPOSE_PROJECTION_MATRIX_NV 0x84E4 +#define GL_FRAGMENT_INPUT_NV 0x936D + +typedef GLuint (QOPENGLF_APIENTRYP PFNGLGENPATHSNVPROC) (GLsizei range); +typedef void (QOPENGLF_APIENTRYP PFNGLDELETEPATHSNVPROC) (GLuint path, GLsizei range); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPATHNVPROC) (GLuint path); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOMMANDSNVPROC) (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOORDSNVPROC) (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSUBCOMMANDSNVPROC) (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSUBCOORDSNVPROC) (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTRINGNVPROC) (GLuint path, GLenum format, GLsizei length, const void *pathString); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHGLYPHSNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHGLYPHRANGENVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (QOPENGLF_APIENTRYP PFNGLWEIGHTPATHSNVPROC) (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); +typedef void (QOPENGLF_APIENTRYP PFNGLCOPYPATHNVPROC) (GLuint resultPath, GLuint srcPath); +typedef void (QOPENGLF_APIENTRYP PFNGLINTERPOLATEPATHSNVPROC) (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); +typedef void (QOPENGLF_APIENTRYP PFNGLTRANSFORMPATHNVPROC) (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, const GLint *value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERINVPROC) (GLuint path, GLenum pname, GLint value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, const GLfloat *value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERFNVPROC) (GLuint path, GLenum pname, GLfloat value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHDASHARRAYNVPROC) (GLuint path, GLsizei dashCount, const GLfloat *dashArray); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTENCILFUNCNVPROC) (GLenum func, GLint ref, GLuint mask); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTENCILDEPTHOFFSETNVPROC) (GLfloat factor, GLfloat units); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOVERDEPTHFUNCNVPROC) (GLenum func); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERFILLPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERSTROKEPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, GLint *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, GLfloat *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOMMANDSNVPROC) (GLuint path, GLubyte *commands); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOORDSNVPROC) (GLuint path, GLfloat *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHDASHARRAYNVPROC) (GLuint path, GLfloat *dashArray); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHMETRICSNVPROC) (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHMETRICRANGENVPROC) (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHSPACINGNVPROC) (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPOINTINFILLPATHNVPROC) (GLuint path, GLuint mask, GLfloat x, GLfloat y); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPOINTINSTROKEPATHNVPROC) (GLuint path, GLfloat x, GLfloat y); +typedef GLfloat (QOPENGLF_APIENTRYP PFNGLGETPATHLENGTHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLPOINTALONGPATHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOAD3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOAD3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULT3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULT3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHGLYPHINDEXRANGENVPROC) (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint baseAndCount[2]); +typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (QOPENGLF_APIENTRYP PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC) (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPROGRAMRESOURCEFVNVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLfloat *params); +#endif + +#ifndef GL_FLAT +#define GL_FLAT 0x1D00 +#endif + +#ifndef GL_INVERT +#define GL_INVERT 0x150A +#endif + +// this one originates from fixed pipeline so may not be in GLES ext headers, but we need it still +#ifndef GL_OBJECT_LINEAR_NV +#define GL_OBJECT_LINEAR_NV 0x2401 +#endif + +#ifndef GL_EXT_direct_state_access +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC) (GLenum mode); +#endif + +// When building on a system with GLES 2.0 or 3.0, we may still compile the NVPR +// code path even though it's never used. Keep it compiling by defining the +// necessary ES 3.1 separable program constants. +#ifndef GL_FRAGMENT_SHADER_BIT +#define GL_FRAGMENT_SHADER_BIT 0x00000002 +#endif +#ifndef GL_UNIFORM +#define GL_UNIFORM 0x92E1 +#endif + +class QQuickNvprFunctionsPrivate; + +class QQuickNvprFunctions +{ +public: + QQuickNvprFunctions(); + ~QQuickNvprFunctions(); + + static QSurfaceFormat format(); + static bool isSupported(); + + bool create(); + + bool createFragmentOnlyPipeline(const char *fragmentShaderSource, GLuint *pipeline, GLuint *program); + + PFNGLGENPATHSNVPROC genPaths = nullptr; + PFNGLDELETEPATHSNVPROC deletePaths = nullptr; + PFNGLISPATHNVPROC isPath = nullptr; + PFNGLPATHCOMMANDSNVPROC pathCommands = nullptr; + PFNGLPATHCOORDSNVPROC pathCoords = nullptr; + PFNGLPATHSUBCOMMANDSNVPROC pathSubCommands = nullptr; + PFNGLPATHSUBCOORDSNVPROC pathSubCoords = nullptr; + PFNGLPATHSTRINGNVPROC pathString = nullptr; + PFNGLPATHGLYPHSNVPROC pathGlyphs = nullptr; + PFNGLPATHGLYPHRANGENVPROC pathGlyphRange = nullptr; + PFNGLWEIGHTPATHSNVPROC weightPaths = nullptr; + PFNGLCOPYPATHNVPROC copyPath = nullptr; + PFNGLINTERPOLATEPATHSNVPROC interpolatePaths = nullptr; + PFNGLTRANSFORMPATHNVPROC transformPath = nullptr; + PFNGLPATHPARAMETERIVNVPROC pathParameteriv = nullptr; + PFNGLPATHPARAMETERINVPROC pathParameteri = nullptr; + PFNGLPATHPARAMETERFVNVPROC pathParameterfv = nullptr; + PFNGLPATHPARAMETERFNVPROC pathParameterf = nullptr; + PFNGLPATHDASHARRAYNVPROC pathDashArray = nullptr; + PFNGLPATHSTENCILFUNCNVPROC pathStencilFunc = nullptr; + PFNGLPATHSTENCILDEPTHOFFSETNVPROC pathStencilDepthOffset = nullptr; + PFNGLSTENCILFILLPATHNVPROC stencilFillPath = nullptr; + PFNGLSTENCILSTROKEPATHNVPROC stencilStrokePath = nullptr; + PFNGLSTENCILFILLPATHINSTANCEDNVPROC stencilFillPathInstanced = nullptr; + PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC stencilStrokePathInstanced = nullptr; + PFNGLPATHCOVERDEPTHFUNCNVPROC pathCoverDepthFunc = nullptr; + PFNGLCOVERFILLPATHNVPROC coverFillPath = nullptr; + PFNGLCOVERSTROKEPATHNVPROC coverStrokePath = nullptr; + PFNGLCOVERFILLPATHINSTANCEDNVPROC coverFillPathInstanced = nullptr; + PFNGLCOVERSTROKEPATHINSTANCEDNVPROC coverStrokePathInstanced = nullptr; + PFNGLGETPATHPARAMETERIVNVPROC getPathParameteriv = nullptr; + PFNGLGETPATHPARAMETERFVNVPROC getPathParameterfv = nullptr; + PFNGLGETPATHCOMMANDSNVPROC getPathCommands = nullptr; + PFNGLGETPATHCOORDSNVPROC getPathCoords = nullptr; + PFNGLGETPATHDASHARRAYNVPROC getPathDashArray = nullptr; + PFNGLGETPATHMETRICSNVPROC getPathMetrics = nullptr; + PFNGLGETPATHMETRICRANGENVPROC getPathMetricRange = nullptr; + PFNGLGETPATHSPACINGNVPROC getPathSpacing = nullptr; + PFNGLISPOINTINFILLPATHNVPROC isPointInFillPath = nullptr; + PFNGLISPOINTINSTROKEPATHNVPROC isPointInStrokePath = nullptr; + PFNGLGETPATHLENGTHNVPROC getPathLength = nullptr; + PFNGLPOINTALONGPATHNVPROC getPointAlongPath = nullptr; + PFNGLMATRIXLOAD3X2FNVPROC matrixLoad3x2f = nullptr; + PFNGLMATRIXLOAD3X3FNVPROC matrixLoad3x3f = nullptr; + PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC matrixLoadTranspose3x3f = nullptr; + PFNGLMATRIXMULT3X2FNVPROC matrixMult3x2f = nullptr; + PFNGLMATRIXMULT3X3FNVPROC matrixMult3x3f = nullptr; + PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC matrixMultTranspose3x3f = nullptr; + PFNGLSTENCILTHENCOVERFILLPATHNVPROC stencilThenCoverFillPath = nullptr; + PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC stencilThenCoverStrokePath = nullptr; + PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC stencilThenCoverFillPathInstanced = nullptr; + PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC stencilThenCoverStrokePathInstanced = nullptr; + PFNGLPATHGLYPHINDEXRANGENVPROC pathGlyphIndexRange = nullptr; + PFNGLPATHGLYPHINDEXARRAYNVPROC pathGlyphIndexArray = nullptr; + PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC pathMemoryGlyphIndexArray = nullptr; + PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC programPathFragmentInputGen = nullptr; + PFNGLGETPROGRAMRESOURCEFVNVPROC getProgramResourcefv = nullptr; + + PFNGLMATRIXLOADFEXTPROC matrixLoadf = nullptr; + PFNGLMATRIXLOADIDENTITYEXTPROC matrixLoadIdentity = nullptr; + +private: + QQuickNvprFunctionsPrivate *d; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_OPENGL + +#endif // QQUICKNVPRFUNCTIONS_P_H diff --git a/src/imports/shapes/qquicknvprfunctions_p_p.h b/src/imports/shapes/qquicknvprfunctions_p_p.h new file mode 100644 index 0000000000..6df20566af --- /dev/null +++ b/src/imports/shapes/qquicknvprfunctions_p_p.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKNVPRFUNCTIONS_P_P_H +#define QQUICKNVPRFUNCTIONS_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qquicknvprfunctions_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickNvprFunctionsPrivate +{ +public: + QQuickNvprFunctionsPrivate(QQuickNvprFunctions *q_ptr) : q(q_ptr) { } + + bool resolve(); + + QQuickNvprFunctions *q; +}; + +QT_END_NAMESPACE + +#endif // QQUICKNVPRFUNCTIONS_P_P_H diff --git a/src/imports/shapes/qquickshape.cpp b/src/imports/shapes/qquickshape.cpp new file mode 100644 index 0000000000..e37addba07 --- /dev/null +++ b/src/imports/shapes/qquickshape.cpp @@ -0,0 +1,1485 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickshape_p.h" +#include "qquickshape_p_p.h" +#include "qquickshapegenericrenderer_p.h" +#include "qquickshapenvprrenderer_p.h" +#include "qquickshapesoftwarerenderer_p.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmlmodule QtQuick.Shapes 1.0 + \title Qt Quick Shapes QML Types + \ingroup qmlmodules + \brief Provides QML types for drawing stroked and filled shapes + + To use the types in this module, import the module with the following line: + + \code + import QtQuick.Shapes 1.0 + \endcode +*/ + +QQuickShapeStrokeFillParams::QQuickShapeStrokeFillParams() + : strokeColor(Qt::white), + strokeWidth(1), + fillColor(Qt::white), + fillRule(QQuickShapePath::OddEvenFill), + joinStyle(QQuickShapePath::BevelJoin), + miterLimit(2), + capStyle(QQuickShapePath::SquareCap), + strokeStyle(QQuickShapePath::SolidLine), + dashOffset(0), + fillGradient(nullptr) +{ + dashPattern << 4 << 2; // 4 * strokeWidth dash followed by 2 * strokeWidth space +} + +QPainterPath QQuickShapePathCommands::toPainterPath() const +{ + QPainterPath p; + int coordIdx = 0; + for (int i = 0; i < cmd.count(); ++i) { + switch (cmd[i]) { + case QQuickShapePathCommands::MoveTo: + p.moveTo(coords[coordIdx], coords[coordIdx + 1]); + coordIdx += 2; + break; + case QQuickShapePathCommands::LineTo: + p.lineTo(coords[coordIdx], coords[coordIdx + 1]); + coordIdx += 2; + break; + case QQuickShapePathCommands::QuadTo: + p.quadTo(coords[coordIdx], coords[coordIdx + 1], + coords[coordIdx + 2], coords[coordIdx + 3]); + coordIdx += 4; + break; + case QQuickShapePathCommands::CubicTo: + p.cubicTo(coords[coordIdx], coords[coordIdx + 1], + coords[coordIdx + 2], coords[coordIdx + 3], + coords[coordIdx + 4], coords[coordIdx + 5]); + coordIdx += 6; + break; + case QQuickShapePathCommands::ArcTo: + // does not map to the QPainterPath API; reuse the helper code from QQuickSvgParser + QQuickSvgParser::pathArc(p, + coords[coordIdx], coords[coordIdx + 1], // radius + coords[coordIdx + 2], // xAxisRotation + !qFuzzyIsNull(coords[coordIdx + 6]), // useLargeArc + !qFuzzyIsNull(coords[coordIdx + 5]), // sweep flag + coords[coordIdx + 3], coords[coordIdx + 4], // end + p.currentPosition().x(), p.currentPosition().y()); + coordIdx += 7; + break; + default: + qWarning("Unknown JS path command: %d", cmd[i]); + break; + } + } + return p; +} + +/*! + \qmltype ShapePath + \instantiates QQuickShapePath + \inqmlmodule QtQuick.Shapes + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Object + \brief Describes a Path and associated properties for stroking and filling + \since 5.10 + + A Shape contains one or more ShapePath elements. At least one + ShapePath is necessary in order to have a Shape output anything + visible. A ShapeItem in turn contains a Path and properties describing the + stroking and filling parameters, such as the stroke width and color, the + fill color or gradient, join and cap styles, and so on. Finally, the Path + object contains a list of path elements like PathMove, PathLine, PathCubic, + PathQuad, PathArc. + + Any property changes in these data sets will be bubble up and change the + output of the Shape. This means that it is simple and easy to change, or + even animate, the starting and ending position, control points, or any + stroke or fill parameters using the usual QML bindings and animation types + like NumberAnimation. + + In the following example the line join style changes automatically based on + the value of joinStyleIndex: + + \code + ShapePath { + strokeColor: "black" + strokeWidth: 16 + fillColor: "transparent" + capStyle: ShapePath.RoundCap + + property int joinStyleIndex: 0 + property variant styles: [ ShapePath.BevelJoin, ShapePath.MiterJoin, ShapePath.RoundJoin ] + + joinStyle: styles[joinStyleIndex] + + Path { + startX: 30 + startY: 30 + PathLine { x: 100; y: 100 } + PathLine { x: 30; y: 100 } + } + } + \endcode + + Once associated with a Shape, here is the output with a joinStyleIndex + of 2 (ShapePath.RoundJoin): + + \image visualpath-code-example.png + */ + +QQuickShapePathPrivate::QQuickShapePathPrivate() + : path(nullptr), + dirty(DirtyAll) +{ +} + +QQuickShapePath::QQuickShapePath(QObject *parent) + : QObject(*(new QQuickShapePathPrivate), parent) +{ +} + +QQuickShapePath::~QQuickShapePath() +{ +} + +/*! + \qmlproperty Path QtQuick.Shapes::ShapePath::path + + This property holds the Path object. + + \default + */ + +QQuickPath *QQuickShapePath::path() const +{ + Q_D(const QQuickShapePath); + return d->path; +} + +void QQuickShapePath::setPath(QQuickPath *path) +{ + Q_D(QQuickShapePath); + if (d->path == path) + return; + + if (d->path) + qmlobject_disconnect(d->path, QQuickPath, SIGNAL(changed()), + this, QQuickShapePath, SLOT(_q_pathChanged())); + d->path = path; + qmlobject_connect(d->path, QQuickPath, SIGNAL(changed()), + this, QQuickShapePath, SLOT(_q_pathChanged())); + + d->dirty |= QQuickShapePathPrivate::DirtyPath; + emit pathChanged(); + emit changed(); +} + +void QQuickShapePathPrivate::_q_pathChanged() +{ + Q_Q(QQuickShapePath); + dirty |= DirtyPath; + emit q->changed(); +} + +/*! + \qmlproperty color QtQuick.Shapes::ShapePath::strokeColor + + This property holds the stroking color. + + When set to \c transparent, no stroking occurs. + + The default value is \c white. + */ + +QColor QQuickShapePath::strokeColor() const +{ + Q_D(const QQuickShapePath); + return d->sfp.strokeColor; +} + +void QQuickShapePath::setStrokeColor(const QColor &color) +{ + Q_D(QQuickShapePath); + if (d->sfp.strokeColor != color) { + d->sfp.strokeColor = color; + d->dirty |= QQuickShapePathPrivate::DirtyStrokeColor; + emit strokeColorChanged(); + emit changed(); + } +} + +/*! + \qmlproperty color QtQuick.Shapes::ShapePath::strokeWidth + + This property holds the stroke width. + + When set to a negative value, no stroking occurs. + + The default value is 1. + */ + +qreal QQuickShapePath::strokeWidth() const +{ + Q_D(const QQuickShapePath); + return d->sfp.strokeWidth; +} + +void QQuickShapePath::setStrokeWidth(qreal w) +{ + Q_D(QQuickShapePath); + if (d->sfp.strokeWidth != w) { + d->sfp.strokeWidth = w; + d->dirty |= QQuickShapePathPrivate::DirtyStrokeWidth; + emit strokeWidthChanged(); + emit changed(); + } +} + +/*! + \qmlproperty color QtQuick.Shapes::ShapePath::fillColor + + This property holds the fill color. + + When set to \c transparent, no filling occurs. + + The default value is \c white. + */ + +QColor QQuickShapePath::fillColor() const +{ + Q_D(const QQuickShapePath); + return d->sfp.fillColor; +} + +void QQuickShapePath::setFillColor(const QColor &color) +{ + Q_D(QQuickShapePath); + if (d->sfp.fillColor != color) { + d->sfp.fillColor = color; + d->dirty |= QQuickShapePathPrivate::DirtyFillColor; + emit fillColorChanged(); + emit changed(); + } +} + +/*! + \qmlproperty enumeration QtQuick.Shapes::ShapePath::fillRule + + This property holds the fill rule. The default value is + ShapePath.OddEvenFill. For an example on fill rules, see + QPainterPath::setFillRule(). + + \list + \li ShapePath.OddEvenFill + \li ShapePath.WindingFill + \endlist + */ + +QQuickShapePath::FillRule QQuickShapePath::fillRule() const +{ + Q_D(const QQuickShapePath); + return d->sfp.fillRule; +} + +void QQuickShapePath::setFillRule(FillRule fillRule) +{ + Q_D(QQuickShapePath); + if (d->sfp.fillRule != fillRule) { + d->sfp.fillRule = fillRule; + d->dirty |= QQuickShapePathPrivate::DirtyFillRule; + emit fillRuleChanged(); + emit changed(); + } +} + +/*! + \qmlproperty enumeration QtQuick.Shapes::ShapePath::joinStyle + + This property defines how joins between two connected lines are drawn. The + default value is ShapePath.BevelJoin. + + \list + \li ShapePath.MiterJoin - The outer edges of the lines are extended to meet at an angle, and this area is filled. + \li ShapePath.BevelJoin - The triangular notch between the two lines is filled. + \li ShapePath.RoundJoin - A circular arc between the two lines is filled. + \endlist + */ + +QQuickShapePath::JoinStyle QQuickShapePath::joinStyle() const +{ + Q_D(const QQuickShapePath); + return d->sfp.joinStyle; +} + +void QQuickShapePath::setJoinStyle(JoinStyle style) +{ + Q_D(QQuickShapePath); + if (d->sfp.joinStyle != style) { + d->sfp.joinStyle = style; + d->dirty |= QQuickShapePathPrivate::DirtyStyle; + emit joinStyleChanged(); + emit changed(); + } +} + +/*! + \qmlproperty int QtQuick.Shapes::ShapePath::miterLimit + + When ShapePath.joinStyle is set to ShapePath.MiterJoin, this property + specifies how far the miter join can extend from the join point. + + The default value is 2. + */ + +int QQuickShapePath::miterLimit() const +{ + Q_D(const QQuickShapePath); + return d->sfp.miterLimit; +} + +void QQuickShapePath::setMiterLimit(int limit) +{ + Q_D(QQuickShapePath); + if (d->sfp.miterLimit != limit) { + d->sfp.miterLimit = limit; + d->dirty |= QQuickShapePathPrivate::DirtyStyle; + emit miterLimitChanged(); + emit changed(); + } +} + +/*! + \qmlproperty enumeration QtQuick.Shapes::ShapePath::capStyle + + This property defines how the end points of lines are drawn. The + default value is ShapePath.SquareCap. + + \list + \li ShapePath.FlatCap - A square line end that does not cover the end point of the line. + \li ShapePath.SquareCap - A square line end that covers the end point and extends beyond it by half the line width. + \li ShapePath.RoundCap - A rounded line end. + \endlist + */ + +QQuickShapePath::CapStyle QQuickShapePath::capStyle() const +{ + Q_D(const QQuickShapePath); + return d->sfp.capStyle; +} + +void QQuickShapePath::setCapStyle(CapStyle style) +{ + Q_D(QQuickShapePath); + if (d->sfp.capStyle != style) { + d->sfp.capStyle = style; + d->dirty |= QQuickShapePathPrivate::DirtyStyle; + emit capStyleChanged(); + emit changed(); + } +} + +/*! + \qmlproperty enumeration QtQuick.Shapes::ShapePath::strokeStyle + + This property defines the style of stroking. The default value is + ShapePath.SolidLine. + + \list + \li ShapePath.SolidLine - A plain line. + \li ShapePath.DashLine - Dashes separated by a few pixels. + \endlist + */ + +QQuickShapePath::StrokeStyle QQuickShapePath::strokeStyle() const +{ + Q_D(const QQuickShapePath); + return d->sfp.strokeStyle; +} + +void QQuickShapePath::setStrokeStyle(StrokeStyle style) +{ + Q_D(QQuickShapePath); + if (d->sfp.strokeStyle != style) { + d->sfp.strokeStyle = style; + d->dirty |= QQuickShapePathPrivate::DirtyDash; + emit strokeStyleChanged(); + emit changed(); + } +} + +/*! + \qmlproperty real QtQuick.Shapes::ShapePath::dashOffset + + This property defines the starting point on the dash pattern, measured in + units used to specify the dash pattern. + + The default value is 0. + + \sa QPen::setDashOffset() + */ + +qreal QQuickShapePath::dashOffset() const +{ + Q_D(const QQuickShapePath); + return d->sfp.dashOffset; +} + +void QQuickShapePath::setDashOffset(qreal offset) +{ + Q_D(QQuickShapePath); + if (d->sfp.dashOffset != offset) { + d->sfp.dashOffset = offset; + d->dirty |= QQuickShapePathPrivate::DirtyDash; + emit dashOffsetChanged(); + emit changed(); + } +} + +/*! + \qmlproperty list QtQuick.Shapes::ShapePath::dashPattern + + This property defines the dash pattern when ShapePath.strokeStyle is set + to ShapePath.DashLine. The pattern must be specified as an even number of + positive entries where the entries 1, 3, 5... are the dashes and 2, 4, 6... + are the spaces. The pattern is specified in units of the pen's width. + + The default value is (4, 2), meaning a dash of 4 * ShapePath.strokeWidth + pixels followed by a space of 2 * ShapePath.strokeWidth pixels. + + \sa QPen::setDashPattern() + */ + +QVector QQuickShapePath::dashPattern() const +{ + Q_D(const QQuickShapePath); + return d->sfp.dashPattern; +} + +void QQuickShapePath::setDashPattern(const QVector &array) +{ + Q_D(QQuickShapePath); + if (d->sfp.dashPattern != array) { + d->sfp.dashPattern = array; + d->dirty |= QQuickShapePathPrivate::DirtyDash; + emit dashPatternChanged(); + emit changed(); + } +} + +/*! + \qmlproperty ShapeGradient QtQuick.Shapes::ShapePath::fillGradient + + This property defines the fill gradient. By default no gradient is enabled + and the value is \c null. In this case the fill uses a solid color based on + the value of ShapePath.fillColor. + + When set, ShapePath.fillColor is ignored and filling is done using one of + the ShapeGradient subtypes. + */ + +QQuickShapeGradient *QQuickShapePath::fillGradient() const +{ + Q_D(const QQuickShapePath); + return d->sfp.fillGradient; +} + +void QQuickShapePath::setFillGradient(QQuickShapeGradient *gradient) +{ + Q_D(QQuickShapePath); + if (d->sfp.fillGradient != gradient) { + if (d->sfp.fillGradient) + qmlobject_disconnect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()), + this, QQuickShapePath, SLOT(_q_fillGradientChanged())); + d->sfp.fillGradient = gradient; + if (d->sfp.fillGradient) + qmlobject_connect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()), + this, QQuickShapePath, SLOT(_q_fillGradientChanged())); + d->dirty |= QQuickShapePathPrivate::DirtyFillGradient; + emit changed(); + } +} + +void QQuickShapePathPrivate::_q_fillGradientChanged() +{ + Q_Q(QQuickShapePath); + dirty |= DirtyFillGradient; + emit q->changed(); +} + +void QQuickShapePath::resetFillGradient() +{ + setFillGradient(nullptr); +} + +/*! + \qmltype Shape + \instantiates QQuickShape + \inqmlmodule QtQuick.Shapes + \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 a GPU vendor 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 + Shape is suitable for creating shapes spreading over larger areas of the + screen, avoiding the performance penalty for texture uploads or framebuffer + blits. In addition, the declarative API allows manipulating, binding to, + and even animating the path element properties like starting and ending + position, the control points, etc. + + The types for specifying path elements are shared between \l PathView and + Shape. However, not all Shape implementations support all path + element types, while some may not make sense for PathView. Shape's + currently supported subset is: PathMove, PathLine, PathQuad, PathCubic, + PathArc, PathSvg. + + See \l Path for a detailed overview of the supported path elements. + + \code + Shape { + width: 200 + height: 150 + anchors.centerIn: parent + ShapePath { + strokeWidth: 4 + strokeColor: "red" + fillGradient: ShapeLinearGradient { + x1: 20; y1: 20 + x2: 180; y2: 130 + ShapeGradientStop { position: 0; color: "blue" } + ShapeGradientStop { position: 0.2; color: "green" } + ShapeGradientStop { position: 0.4; color: "red" } + ShapeGradientStop { position: 0.6; color: "yellow" } + ShapeGradientStop { position: 1; color: "cyan" } + } + strokeStyle: ShapePath.DashLine + dashPattern: [ 1, 4 ] + Path { + startX: 20; startY: 20 + PathLine { x: 180; y: 130 } + PathLine { x: 20; y: 130 } + PathLine { x: 20; y: 20 } + } + } + } + \endcode + + \image pathitem-code-example.png + + \note It is important to be aware of performance implications, in particular + when the application is running on the generic Shape 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 Shape itself all lead + to retriangulation of the affected elements on every change. Therefore, + applying animation to such properties can 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 However, the data-driven, declarative nature of the Shape API often + means better cacheability for the underlying CPU and GPU resources. A + property change in one ShapePath will only lead to reprocessing the affected + ShapePath, leaving other parts of the Shape unchanged. Therefore, a heavily + changing (for example, animating) property can often result in a lower + overall system load than with imperative painting approaches (for example, + QPainter). + + The following list summarizes the available Shape rendering approaches: + + \list + + \li When running with the default, OpenGL backend of Qt Quick, both the + generic, triangulation-based and the NVIDIA-specific + \c{GL_NV_path_rendering} methods are available. The choice is made at + runtime, depending on the graphics driver's capabilities. When this is not + desired, applications can force using the generic method by setting the + Shape.enableVendorExtensions property to \c false. + + \li The \c software backend is fully supported. The path is rendered via + QPainter::strokePath() and QPainter::fillPath() in this case. + + \li The Direct 3D 12 backend is not currently supported. + + \li The OpenVG backend is not currently supported. + + \endlist + + \sa Path, PathMove, PathLine, PathQuad, PathCubic, PathArc, PathSvg +*/ + +QQuickShapePrivate::QQuickShapePrivate() + : componentComplete(true), + spChanged(false), + rendererType(QQuickShape::UnknownRenderer), + async(false), + status(QQuickShape::Null), + renderer(nullptr), + enableVendorExts(true) +{ +} + +QQuickShapePrivate::~QQuickShapePrivate() +{ + delete renderer; +} + +void QQuickShapePrivate::_q_shapePathChanged() +{ + Q_Q(QQuickShape); + spChanged = true; + q->polish(); +} + +void QQuickShapePrivate::setStatus(QQuickShape::Status newStatus) +{ + Q_Q(QQuickShape); + if (status != newStatus) { + status = newStatus; + emit q->statusChanged(); + } +} + +QQuickShape::QQuickShape(QQuickItem *parent) + : QQuickItem(*(new QQuickShapePrivate), parent) +{ + setFlag(ItemHasContents); +} + +QQuickShape::~QQuickShape() +{ +} + +/*! + \qmlproperty enumeration QtQuick.Shapes::Shape::rendererType + + This property determines which path rendering backend is active. + + \list + + \li Shape.UnknownRenderer - The renderer is unknown. + + \li Shape.GeometryRenderer - The generic, driver independent solution + for OpenGL. Uses the same CPU-based triangulation approach as QPainter's + OpenGL 2 paint engine. This is the default on non-NVIDIA hardware when the + default, OpenGL Qt Quick scenegraph backend is in use. + + \li Shape.NvprRenderer - Path items are rendered by performing OpenGL + calls using the \c{GL_NV_path_rendering} extension. This is the default on + NVIDIA hardware when the default, OpenGL Qt Quick scenegraph backend is in + use. + + \li Shape.SoftwareRenderer - Pure QPainter drawing using the raster + paint engine. This is the default, and only, option when the Qt Quick + scenegraph is running with the \c software backend. + + \endlist +*/ + +QQuickShape::RendererType QQuickShape::rendererType() const +{ + Q_D(const QQuickShape); + return d->rendererType; +} + +/*! + \qmlproperty bool QtQuick.Shapes::Shape::asynchronous + + When Shape.rendererType is Shape.GeometryRenderer, the input path is + triangulated on the CPU during the polishing phase of the Shape. This is + potentially expensive. To offload this work to separate worker threads, set + this property to \c true. + + When enabled, making a Shape visible will not wait for the content to + become available. Instead, the gui/main thread is not blocked and the + results of the path rendering are shown only when all the asynchronous work + has been finished. + + The default value is \c false. + */ + +bool QQuickShape::asynchronous() const +{ + Q_D(const QQuickShape); + return d->async; +} + +void QQuickShape::setAsynchronous(bool async) +{ + Q_D(QQuickShape); + if (d->async != async) { + d->async = async; + emit asynchronousChanged(); + if (d->componentComplete) + d->_q_shapePathChanged(); + } +} + +/*! + \qmlproperty bool QtQuick.Shapes::Shape::enableVendorExtensions + + This property controls the usage of non-standard OpenGL extensions like + GL_NV_path_rendering. To disable Shape.NvprRenderer and force a uniform + behavior regardless of the graphics card and drivers, set this property to + \c false. + + The default value is \c true. + */ + +bool QQuickShape::enableVendorExtensions() const +{ + Q_D(const QQuickShape); + return d->enableVendorExts; +} + +void QQuickShape::setEnableVendorExtensions(bool enable) +{ + Q_D(QQuickShape); + if (d->enableVendorExts != enable) { + d->enableVendorExts = enable; + emit enableVendorExtensionsChanged(); + } +} + +/*! + \qmlproperty enumeration QtQuick.Shapes::Shape::status + + This property determines the status of the Shape and is relevant when + Shape.asynchronous is set to \c true. + + \list + + \li Shape.Null - Not yet initialized. + + \li Shape.Ready - The Shape has finished processing. + + \li Shape.Processing - The path is being processed. + + \endlist + */ + +QQuickShape::Status QQuickShape::status() const +{ + Q_D(const QQuickShape); + return d->status; +} + +static QQuickShapePath *vpe_at(QQmlListProperty *property, int index) +{ + QQuickShapePrivate *d = QQuickShapePrivate::get(static_cast(property->object)); + return d->qmlData.sp.at(index); +} + +static void vpe_append(QQmlListProperty *property, QQuickShapePath *obj) +{ + QQuickShape *item = static_cast(property->object); + QQuickShapePrivate *d = QQuickShapePrivate::get(item); + d->qmlData.sp.append(obj); + + if (d->componentComplete) { + QObject::connect(obj, SIGNAL(changed()), item, SLOT(_q_shapePathChanged())); + d->_q_shapePathChanged(); + } +} + +static int vpe_count(QQmlListProperty *property) +{ + QQuickShapePrivate *d = QQuickShapePrivate::get(static_cast(property->object)); + return d->qmlData.sp.count(); +} + +static void vpe_clear(QQmlListProperty *property) +{ + QQuickShape *item = static_cast(property->object); + QQuickShapePrivate *d = QQuickShapePrivate::get(item); + + for (QQuickShapePath *p : d->qmlData.sp) + QObject::disconnect(p, SIGNAL(changed()), item, SLOT(_q_shapePathChanged())); + + d->qmlData.sp.clear(); + + if (d->componentComplete) + d->_q_shapePathChanged(); +} + +/*! + \qmlproperty list QtQuick.Shapes::Shape::elements + + This property holds the ShapePath objects that define the contents of the + Shape. + + \default + */ + +QQmlListProperty QQuickShape::elements() +{ + return QQmlListProperty(this, + nullptr, + vpe_append, + vpe_count, + vpe_at, + vpe_clear); +} + +void QQuickShape::classBegin() +{ + Q_D(QQuickShape); + d->componentComplete = false; +} + +void QQuickShape::componentComplete() +{ + Q_D(QQuickShape); + d->componentComplete = true; + + for (QQuickShapePath *p : d->qmlData.sp) + connect(p, SIGNAL(changed()), this, SLOT(_q_shapePathChanged())); + + d->_q_shapePathChanged(); +} + +void QQuickShape::updatePolish() +{ + Q_D(QQuickShape); + + if (!d->spChanged) + return; + + d->spChanged = false; + + if (!d->renderer) { + d->createRenderer(); + if (!d->renderer) + return; + emit rendererChanged(); + } + + // endSync() is where expensive calculations may happen (or get kicked off + // on worker threads), depending on the backend. Therefore do this only + // when the item is visible. + if (isVisible()) + d->sync(); + + update(); +} + +void QQuickShape::itemChange(ItemChange change, const ItemChangeData &data) +{ + Q_D(QQuickShape); + + // sync may have been deferred; do it now if the item became visible + if (change == ItemVisibleHasChanged && data.boolValue) + d->_q_shapePathChanged(); + + QQuickItem::itemChange(change, data); +} + +QSGNode *QQuickShape::updatePaintNode(QSGNode *node, UpdatePaintNodeData *) +{ + // Called on the render thread, with the gui thread blocked. We can now + // safely access gui thread data. + + Q_D(QQuickShape); + if (d->renderer) { + if (!node) + node = d->createNode(); + d->renderer->updateNode(); + } + return node; +} + +// the renderer object lives on the gui thread +void QQuickShapePrivate::createRenderer() +{ + Q_Q(QQuickShape); + QSGRendererInterface *ri = q->window()->rendererInterface(); + if (!ri) + return; + + switch (ri->graphicsApi()) { +#ifndef QT_NO_OPENGL + case QSGRendererInterface::OpenGL: + if (enableVendorExts && QQuickShapeNvprRenderNode::isSupported()) { + rendererType = QQuickShape::NvprRenderer; + renderer = new QQuickShapeNvprRenderer; + } else { + rendererType = QQuickShape::GeometryRenderer; + renderer = new QQuickShapeGenericRenderer(q); + } + break; +#endif + case QSGRendererInterface::Software: + rendererType = QQuickShape::SoftwareRenderer; + renderer = new QQuickShapeSoftwareRenderer; + break; + default: + qWarning("No path backend for this graphics API yet"); + break; + } +} + +// the node lives on the render thread +QSGNode *QQuickShapePrivate::createNode() +{ + Q_Q(QQuickShape); + QSGNode *node = nullptr; + if (!q->window()) + return node; + QSGRendererInterface *ri = q->window()->rendererInterface(); + if (!ri) + return node; + + switch (ri->graphicsApi()) { +#ifndef QT_NO_OPENGL + case QSGRendererInterface::OpenGL: + if (enableVendorExts && QQuickShapeNvprRenderNode::isSupported()) { + node = new QQuickShapeNvprRenderNode; + static_cast(renderer)->setNode( + static_cast(node)); + } else { + node = new QQuickShapeGenericNode; + static_cast(renderer)->setRootNode( + static_cast(node)); + } + break; +#endif + case QSGRendererInterface::Software: + node = new QQuickShapeSoftwareRenderNode(q); + static_cast(renderer)->setNode( + static_cast(node)); + break; + default: + qWarning("No path backend for this graphics API yet"); + break; + } + + return node; +} + +static void q_asyncShapeReady(void *data) +{ + QQuickShapePrivate *self = static_cast(data); + self->setStatus(QQuickShape::Ready); +} + +void QQuickShapePrivate::sync() +{ + const bool useAsync = async && renderer->flags().testFlag(QQuickAbstractPathRenderer::SupportsAsync); + if (useAsync) { + setStatus(QQuickShape::Processing); + renderer->setAsyncCallback(q_asyncShapeReady, this); + } + + if (!jsData.isValid()) { + // Standard route: The path and stroke/fill parameters are provided via + // QML elements. + const int count = qmlData.sp.count(); + renderer->beginSync(count); + + for (int i = 0; i < count; ++i) { + QQuickShapePath *p = qmlData.sp[i]; + int &dirty(QQuickShapePathPrivate::get(p)->dirty); + + if (dirty & QQuickShapePathPrivate::DirtyPath) + renderer->setPath(i, p->path()); + if (dirty & QQuickShapePathPrivate::DirtyStrokeColor) + renderer->setStrokeColor(i, p->strokeColor()); + if (dirty & QQuickShapePathPrivate::DirtyStrokeWidth) + renderer->setStrokeWidth(i, p->strokeWidth()); + if (dirty & QQuickShapePathPrivate::DirtyFillColor) + renderer->setFillColor(i, p->fillColor()); + if (dirty & QQuickShapePathPrivate::DirtyFillRule) + renderer->setFillRule(i, p->fillRule()); + if (dirty & QQuickShapePathPrivate::DirtyStyle) { + renderer->setJoinStyle(i, p->joinStyle(), p->miterLimit()); + renderer->setCapStyle(i, p->capStyle()); + } + if (dirty & QQuickShapePathPrivate::DirtyDash) + renderer->setStrokeStyle(i, p->strokeStyle(), p->dashOffset(), p->dashPattern()); + if (dirty & QQuickShapePathPrivate::DirtyFillGradient) + renderer->setFillGradient(i, p->fillGradient()); + + dirty = 0; + } + + renderer->endSync(useAsync); + } else { + + // ### there is no public API to reach this code path atm + Q_UNREACHABLE(); + + // Path and stroke/fill params provided from JavaScript. This avoids + // QObjects at the expense of not supporting changes afterwards. + const int count = jsData.paths.count(); + renderer->beginSync(count); + + for (int i = 0; i < count; ++i) { + renderer->setJSPath(i, jsData.paths[i]); + const QQuickShapeStrokeFillParams sfp(jsData.sfp[i]); + renderer->setStrokeColor(i, sfp.strokeColor); + renderer->setStrokeWidth(i, sfp.strokeWidth); + renderer->setFillColor(i, sfp.fillColor); + renderer->setFillRule(i, sfp.fillRule); + renderer->setJoinStyle(i, sfp.joinStyle, sfp.miterLimit); + renderer->setCapStyle(i, sfp.capStyle); + renderer->setStrokeStyle(i, sfp.strokeStyle, sfp.dashOffset, sfp.dashPattern); + renderer->setFillGradient(i, sfp.fillGradient); + } + + renderer->endSync(useAsync); + } + + if (!useAsync) + setStatus(QQuickShape::Ready); +} + +// ***** gradient support ***** + +/*! + \qmltype ShapeGradientStop + \instantiates QQuickShapeGradientStop + \inqmlmodule QtQuick.Shapes + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Object + \brief Defines a color at a position in a gradient + \since 5.10 + */ + +QQuickShapeGradientStop::QQuickShapeGradientStop(QObject *parent) + : QObject(parent), + m_position(0), + m_color(Qt::black) +{ +} + +/*! + \qmlproperty real QtQuick.Shapes::ShapeGradientStop::position + + The position and color properties describe the color used at a given + position in a gradient, as represented by a gradient stop. + + The default value is 0. + */ + +qreal QQuickShapeGradientStop::position() const +{ + return m_position; +} + +void QQuickShapeGradientStop::setPosition(qreal position) +{ + if (m_position != position) { + m_position = position; + if (QQuickShapeGradient *grad = qobject_cast(parent())) + emit grad->updated(); + } +} + +/*! + \qmlproperty real QtQuick.Shapes::ShapeGradientStop::color + + The position and color properties describe the color used at a given + position in a gradient, as represented by a gradient stop. + + The default value is \c black. + */ + +QColor QQuickShapeGradientStop::color() const +{ + return m_color; +} + +void QQuickShapeGradientStop::setColor(const QColor &color) +{ + if (m_color != color) { + m_color = color; + if (QQuickShapeGradient *grad = qobject_cast(parent())) + emit grad->updated(); + } +} + +/*! + \qmltype ShapeGradient + \instantiates QQuickShapeGradient + \inqmlmodule QtQuick.Shapes + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Object + \brief Base type of Shape fill gradients + \since 5.10 + + This is an abstract base class for gradients like ShapeLinearGradient and + cannot be created directly. + */ + +QQuickShapeGradient::QQuickShapeGradient(QObject *parent) + : QObject(parent), + m_spread(PadSpread) +{ +} + +int QQuickShapeGradient::countStops(QQmlListProperty *list) +{ + QQuickShapeGradient *grad = qobject_cast(list->object); + Q_ASSERT(grad); + return grad->m_stops.count(); +} + +QObject *QQuickShapeGradient::atStop(QQmlListProperty *list, int index) +{ + QQuickShapeGradient *grad = qobject_cast(list->object); + Q_ASSERT(grad); + return grad->m_stops.at(index); +} + +void QQuickShapeGradient::appendStop(QQmlListProperty *list, QObject *stop) +{ + QQuickShapeGradientStop *sstop = qobject_cast(stop); + if (!sstop) { + qWarning("Gradient stop list only supports QQuickShapeGradientStop elements"); + return; + } + QQuickShapeGradient *grad = qobject_cast(list->object); + Q_ASSERT(grad); + sstop->setParent(grad); + grad->m_stops.append(sstop); +} + +/*! + \qmlproperty list QtQuick.Shapes::ShapeGradient::stops + \default + + The list of ShapeGradientStop objects defining the colors at given positions + in the gradient. + */ + +QQmlListProperty QQuickShapeGradient::stops() +{ + return QQmlListProperty(this, nullptr, + &QQuickShapeGradient::appendStop, + &QQuickShapeGradient::countStops, + &QQuickShapeGradient::atStop, + nullptr); +} + +QGradientStops QQuickShapeGradient::sortedGradientStops() const +{ + QGradientStops result; + for (int i = 0; i < m_stops.count(); ++i) { + QQuickShapeGradientStop *s = static_cast(m_stops[i]); + int j = 0; + while (j < result.count() && result[j].first < s->position()) + ++j; + result.insert(j, QGradientStop(s->position(), s->color())); + } + return result; +} + +/*! + \qmlproperty enumeration QtQuick.Shapes::ShapeGradient::spred + + Specifies how the area outside the gradient area should be filled. The + default value is ShapeGradient.PadSpread. + + \list + \li ShapeGradient.PadSpread - The area is filled with the closest stop color. + \li ShapeGradient.RepeatSpread - The gradient is repeated outside the gradient area. + \li ShapeGradient.ReflectSpread - The gradient is reflected outside the gradient area. + \endlist + */ + +QQuickShapeGradient::SpreadMode QQuickShapeGradient::spread() const +{ + return m_spread; +} + +void QQuickShapeGradient::setSpread(SpreadMode mode) +{ + if (m_spread != mode) { + m_spread = mode; + emit spreadChanged(); + emit updated(); + } +} + +/*! + \qmltype ShapeLinearGradient + \instantiates QQuickShapeLinearGradient + \inqmlmodule QtQuick.Shapes + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits ShapeGradient + \brief Linear gradient + \since 5.10 + + Linear gradients interpolate colors between start and end points. Outside + these points the gradient is either padded, reflected or repeated depending + on the spread type. + + \sa QLinearGradient + */ + +QQuickShapeLinearGradient::QQuickShapeLinearGradient(QObject *parent) + : QQuickShapeGradient(parent) +{ +} + +/*! + \qmlproperty real QtQuick.Shapes::ShapeLinearGradient::x1 + \qmlproperty real QtQuick.Shapes::ShapeLinearGradient::y1 + \qmlproperty real QtQuick.Shapes::ShapeLinearGradient::x2 + \qmlproperty real QtQuick.Shapes::ShapeLinearGradient::y2 + + These properties define the start and end points between which color + interpolation occurs. By default both the stard and end points are set to + (0, 0). + */ + +qreal QQuickShapeLinearGradient::x1() const +{ + return m_start.x(); +} + +void QQuickShapeLinearGradient::setX1(qreal v) +{ + if (m_start.x() != v) { + m_start.setX(v); + emit x1Changed(); + emit updated(); + } +} + +qreal QQuickShapeLinearGradient::y1() const +{ + return m_start.y(); +} + +void QQuickShapeLinearGradient::setY1(qreal v) +{ + if (m_start.y() != v) { + m_start.setY(v); + emit y1Changed(); + emit updated(); + } +} + +qreal QQuickShapeLinearGradient::x2() const +{ + return m_end.x(); +} + +void QQuickShapeLinearGradient::setX2(qreal v) +{ + if (m_end.x() != v) { + m_end.setX(v); + emit x2Changed(); + emit updated(); + } +} + +qreal QQuickShapeLinearGradient::y2() const +{ + return m_end.y(); +} + +void QQuickShapeLinearGradient::setY2(qreal v) +{ + if (m_end.y() != v) { + m_end.setY(v); + emit y2Changed(); + emit updated(); + } +} + +#ifndef QT_NO_OPENGL + +// contexts sharing with each other get the same cache instance +class QQuickShapeGradientCacheWrapper +{ +public: + QQuickShapeGradientCache *get(QOpenGLContext *context) + { + return m_resource.value(context); + } + +private: + QOpenGLMultiGroupSharedResource m_resource; +}; + +QQuickShapeGradientCache *QQuickShapeGradientCache::currentCache() +{ + static QQuickShapeGradientCacheWrapper qt_path_gradient_caches; + return qt_path_gradient_caches.get(QOpenGLContext::currentContext()); +} + +// let QOpenGLContext manage the lifetime of the cached textures +QQuickShapeGradientCache::~QQuickShapeGradientCache() +{ + m_cache.clear(); +} + +void QQuickShapeGradientCache::invalidateResource() +{ + m_cache.clear(); +} + +void QQuickShapeGradientCache::freeResource(QOpenGLContext *) +{ + qDeleteAll(m_cache); + m_cache.clear(); +} + +static void generateGradientColorTable(const QQuickShapeGradientCache::GradientDesc &gradient, + uint *colorTable, int size, float opacity) +{ + int pos = 0; + const QGradientStops &s = gradient.stops; + const bool colorInterpolation = true; + + uint alpha = qRound(opacity * 256); + uint current_color = ARGB_COMBINE_ALPHA(s[0].second.rgba(), alpha); + qreal incr = 1.0 / qreal(size); + qreal fpos = 1.5 * incr; + colorTable[pos++] = ARGB2RGBA(qPremultiply(current_color)); + + while (fpos <= s.first().first) { + colorTable[pos] = colorTable[pos - 1]; + pos++; + fpos += incr; + } + + if (colorInterpolation) + current_color = qPremultiply(current_color); + + const int sLast = s.size() - 1; + for (int i = 0; i < sLast; ++i) { + qreal delta = 1/(s[i+1].first - s[i].first); + uint next_color = ARGB_COMBINE_ALPHA(s[i + 1].second.rgba(), alpha); + if (colorInterpolation) + next_color = qPremultiply(next_color); + + while (fpos < s[i+1].first && pos < size) { + int dist = int(256 * ((fpos - s[i].first) * delta)); + int idist = 256 - dist; + if (colorInterpolation) + colorTable[pos] = ARGB2RGBA(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist)); + else + colorTable[pos] = ARGB2RGBA(qPremultiply(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist))); + ++pos; + fpos += incr; + } + current_color = next_color; + } + + Q_ASSERT(s.size() > 0); + + uint last_color = ARGB2RGBA(qPremultiply(ARGB_COMBINE_ALPHA(s[sLast].second.rgba(), alpha))); + for ( ; pos < size; ++pos) + colorTable[pos] = last_color; + + colorTable[size-1] = last_color; +} + +QSGTexture *QQuickShapeGradientCache::get(const GradientDesc &grad) +{ + QSGPlainTexture *tx = m_cache[grad]; + if (!tx) { + QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); + GLuint id; + f->glGenTextures(1, &id); + f->glBindTexture(GL_TEXTURE_2D, id); + static const uint W = 1024; // texture size is 1024x1 + uint buf[W]; + generateGradientColorTable(grad, buf, W, 1.0f); + f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, W, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf); + tx = new QSGPlainTexture; + tx->setTextureId(id); + switch (grad.spread) { + case QQuickShapeGradient::PadSpread: + tx->setHorizontalWrapMode(QSGTexture::ClampToEdge); + tx->setVerticalWrapMode(QSGTexture::ClampToEdge); + break; + case QQuickShapeGradient::RepeatSpread: + tx->setHorizontalWrapMode(QSGTexture::Repeat); + tx->setVerticalWrapMode(QSGTexture::Repeat); + break; + case QQuickShapeGradient::ReflectSpread: + tx->setHorizontalWrapMode(QSGTexture::MirroredRepeat); + tx->setVerticalWrapMode(QSGTexture::MirroredRepeat); + break; + default: + qWarning("Unknown gradient spread mode %d", grad.spread); + break; + } + m_cache[grad] = tx; + } + return tx; +} + +#endif // QT_NO_OPENGL + +QT_END_NAMESPACE + +#include "moc_qquickshape_p.cpp" diff --git a/src/imports/shapes/qquickshape_p.h b/src/imports/shapes/qquickshape_p.h new file mode 100644 index 0000000000..5b3580dd1b --- /dev/null +++ b/src/imports/shapes/qquickshape_p.h @@ -0,0 +1,327 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSHAPE_P_H +#define QQUICKSHAPE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickitem.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickShapePathPrivate; +class QQuickShapePrivate; + +class QQuickShapeGradientStop : public QObject +{ + Q_OBJECT + Q_PROPERTY(qreal position READ position WRITE setPosition) + Q_PROPERTY(QColor color READ color WRITE setColor) + +public: + QQuickShapeGradientStop(QObject *parent = nullptr); + + qreal position() const; + void setPosition(qreal position); + + QColor color() const; + void setColor(const QColor &color); + +private: + qreal m_position; + QColor m_color; +}; + +class QQuickShapeGradient : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQmlListProperty stops READ stops) + Q_PROPERTY(SpreadMode spread READ spread WRITE setSpread NOTIFY spreadChanged) + Q_CLASSINFO("DefaultProperty", "stops") + +public: + enum SpreadMode { + PadSpread, + RepeatSpread, + ReflectSpread + }; + Q_ENUM(SpreadMode) + + QQuickShapeGradient(QObject *parent = nullptr); + + QQmlListProperty stops(); + + QGradientStops sortedGradientStops() const; + + SpreadMode spread() const; + void setSpread(SpreadMode mode); + +signals: + void updated(); + void spreadChanged(); + +private: + static int countStops(QQmlListProperty *list); + static QObject *atStop(QQmlListProperty *list, int index); + static void appendStop(QQmlListProperty *list, QObject *stop); + + QVector m_stops; + SpreadMode m_spread; +}; + +class QQuickShapeLinearGradient : public QQuickShapeGradient +{ + Q_OBJECT + Q_PROPERTY(qreal x1 READ x1 WRITE setX1 NOTIFY x1Changed) + Q_PROPERTY(qreal y1 READ y1 WRITE setY1 NOTIFY y1Changed) + Q_PROPERTY(qreal x2 READ x2 WRITE setX2 NOTIFY x2Changed) + Q_PROPERTY(qreal y2 READ y2 WRITE setY2 NOTIFY y2Changed) + Q_CLASSINFO("DefaultProperty", "stops") + +public: + QQuickShapeLinearGradient(QObject *parent = nullptr); + + qreal x1() const; + void setX1(qreal v); + qreal y1() const; + void setY1(qreal v); + qreal x2() const; + void setX2(qreal v); + qreal y2() const; + void setY2(qreal v); + +signals: + void x1Changed(); + void y1Changed(); + void x2Changed(); + void y2Changed(); + +private: + QPointF m_start; + QPointF m_end; +}; + +class QQuickShapePath : public QObject +{ + Q_OBJECT + + 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) + Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged) + Q_PROPERTY(FillRule fillRule READ fillRule WRITE setFillRule NOTIFY fillRuleChanged) + Q_PROPERTY(JoinStyle joinStyle READ joinStyle WRITE setJoinStyle NOTIFY joinStyleChanged) + Q_PROPERTY(int miterLimit READ miterLimit WRITE setMiterLimit NOTIFY miterLimitChanged) + Q_PROPERTY(CapStyle capStyle READ capStyle WRITE setCapStyle NOTIFY capStyleChanged) + Q_PROPERTY(StrokeStyle strokeStyle READ strokeStyle WRITE setStrokeStyle NOTIFY strokeStyleChanged) + Q_PROPERTY(qreal dashOffset READ dashOffset WRITE setDashOffset NOTIFY dashOffsetChanged) + Q_PROPERTY(QVector dashPattern READ dashPattern WRITE setDashPattern NOTIFY dashPatternChanged) + Q_PROPERTY(QQuickShapeGradient *fillGradient READ fillGradient WRITE setFillGradient RESET resetFillGradient) + +public: + enum FillRule { + OddEvenFill = Qt::OddEvenFill, + WindingFill = Qt::WindingFill + }; + Q_ENUM(FillRule) + + enum JoinStyle { + MiterJoin = Qt::MiterJoin, + BevelJoin = Qt::BevelJoin, + RoundJoin = Qt::RoundJoin + }; + Q_ENUM(JoinStyle) + + enum CapStyle { + FlatCap = Qt::FlatCap, + SquareCap = Qt::SquareCap, + RoundCap = Qt::RoundCap + }; + Q_ENUM(CapStyle) + + enum StrokeStyle { + SolidLine = Qt::SolidLine, + DashLine = Qt::DashLine + }; + Q_ENUM(StrokeStyle) + + QQuickShapePath(QObject *parent = nullptr); + ~QQuickShapePath(); + + QQuickPath *path() const; + void setPath(QQuickPath *path); + + QColor strokeColor() const; + void setStrokeColor(const QColor &color); + + qreal strokeWidth() const; + void setStrokeWidth(qreal w); + + QColor fillColor() const; + void setFillColor(const QColor &color); + + FillRule fillRule() const; + void setFillRule(FillRule fillRule); + + JoinStyle joinStyle() const; + void setJoinStyle(JoinStyle style); + + int miterLimit() const; + void setMiterLimit(int limit); + + CapStyle capStyle() const; + void setCapStyle(CapStyle style); + + StrokeStyle strokeStyle() const; + void setStrokeStyle(StrokeStyle style); + + qreal dashOffset() const; + void setDashOffset(qreal offset); + + QVector dashPattern() const; + void setDashPattern(const QVector &array); + + QQuickShapeGradient *fillGradient() const; + void setFillGradient(QQuickShapeGradient *gradient); + void resetFillGradient(); + +Q_SIGNALS: + void changed(); + void pathChanged(); + void strokeColorChanged(); + void strokeWidthChanged(); + void fillColorChanged(); + void fillRuleChanged(); + void joinStyleChanged(); + void miterLimitChanged(); + void capStyleChanged(); + void strokeStyleChanged(); + void dashOffsetChanged(); + void dashPatternChanged(); + void fillGradientChanged(); + +private: + Q_DISABLE_COPY(QQuickShapePath) + Q_DECLARE_PRIVATE(QQuickShapePath) + Q_PRIVATE_SLOT(d_func(), void _q_pathChanged()) + Q_PRIVATE_SLOT(d_func(), void _q_fillGradientChanged()) +}; + +class QQuickShape : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged) + Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) + Q_PROPERTY(bool enableVendorExtensions READ enableVendorExtensions WRITE setEnableVendorExtensions NOTIFY enableVendorExtensionsChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(QQmlListProperty elements READ elements) + Q_CLASSINFO("DefaultProperty", "elements") + +public: + enum RendererType { + UnknownRenderer, + GeometryRenderer, + NvprRenderer, + SoftwareRenderer + }; + Q_ENUM(RendererType) + + enum Status { + Null, + Ready, + Processing + }; + Q_ENUM(Status) + + QQuickShape(QQuickItem *parent = nullptr); + ~QQuickShape(); + + RendererType rendererType() const; + + bool asynchronous() const; + void setAsynchronous(bool async); + + bool enableVendorExtensions() const; + void setEnableVendorExtensions(bool enable); + + Status status() const; + + QQmlListProperty elements(); + +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(); + void asynchronousChanged(); + void enableVendorExtensionsChanged(); + void statusChanged(); + +private: + Q_DISABLE_COPY(QQuickShape) + Q_DECLARE_PRIVATE(QQuickShape) + Q_PRIVATE_SLOT(d_func(), void _q_shapePathChanged()) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickShape) + +#endif // QQUICKSHAPE_P_H diff --git a/src/imports/shapes/qquickshape_p_p.h b/src/imports/shapes/qquickshape_p_p.h new file mode 100644 index 0000000000..7a503e36a9 --- /dev/null +++ b/src/imports/shapes/qquickshape_p_p.h @@ -0,0 +1,282 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSHAPE_P_P_H +#define QQUICKSHAPE_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickshape_p.h" +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QSGPlainTexture; + +struct QQuickShapePathCommands +{ + enum Command { + MoveTo, + LineTo, + QuadTo, + CubicTo, + ArcTo + }; + + QVector cmd; + QVector coords; + + QPainterPath toPainterPath() const; +}; + +struct QQuickShapeStrokeFillParams +{ + QQuickShapeStrokeFillParams(); + + QColor strokeColor; + qreal strokeWidth; + QColor fillColor; + QQuickShapePath::FillRule fillRule; + QQuickShapePath::JoinStyle joinStyle; + int miterLimit; + QQuickShapePath::CapStyle capStyle; + QQuickShapePath::StrokeStyle strokeStyle; + qreal dashOffset; + QVector dashPattern; + QQuickShapeGradient *fillGradient; +}; + +class QQuickAbstractPathRenderer +{ +public: + enum Flag { + SupportsAsync = 0x01 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + virtual ~QQuickAbstractPathRenderer() { } + + // Gui thread + virtual void beginSync(int totalCount) = 0; + virtual void endSync(bool async) = 0; + virtual void setAsyncCallback(void (*)(void *), void *) { } + virtual Flags flags() const { return 0; } + // - QML API + virtual void setPath(int index, const QQuickPath *path) = 0; + // - JS API + virtual void setJSPath(int index, const QQuickShapePathCommands &path) = 0; + // - stroke/fill parameters + 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, QQuickShapePath::FillRule fillRule) = 0; + virtual void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) = 0; + virtual void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) = 0; + virtual void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) = 0; + virtual void setFillGradient(int index, QQuickShapeGradient *gradient) = 0; + + // Render thread, with gui blocked + virtual void updateNode() = 0; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickAbstractPathRenderer::Flags) + +class QQuickShapePathPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QQuickShapePath) + +public: + enum Dirty { + DirtyPath = 0x01, + DirtyStrokeColor = 0x02, + DirtyStrokeWidth = 0x04, + DirtyFillColor = 0x08, + DirtyFillRule = 0x10, + DirtyStyle = 0x20, + DirtyDash = 0x40, + DirtyFillGradient = 0x80, + + DirtyAll = 0xFF + }; + + QQuickShapePathPrivate(); + + void _q_pathChanged(); + void _q_fillGradientChanged(); + + static QQuickShapePathPrivate *get(QQuickShapePath *p) { return p->d_func(); } + + QQuickPath *path; + int dirty; + QQuickShapeStrokeFillParams sfp; +}; + +class QQuickShapePrivate : public QQuickItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickShape) + +public: + QQuickShapePrivate(); + ~QQuickShapePrivate(); + + void createRenderer(); + QSGNode *createNode(); + void sync(); + + void _q_shapePathChanged(); + void setStatus(QQuickShape::Status newStatus); + + static QQuickShapePrivate *get(QQuickShape *item) { return item->d_func(); } + + bool componentComplete; + bool spChanged; + QQuickShape::RendererType rendererType; + bool async; + QQuickShape::Status status; + QQuickAbstractPathRenderer *renderer; + + struct { + QVector sp; + } qmlData; + + struct { + bool isValid() const { Q_ASSERT(paths.count() == sfp.count()); return !paths.isEmpty(); } + QVector paths; + QVector sfp; + } jsData; + + bool enableVendorExts; +}; + +class QQuickShapePathObject : public QObject +{ + Q_OBJECT + +public: + QQuickShapePathObject(QObject *parent = nullptr) : QObject(parent) { } + + void setV4Engine(QV4::ExecutionEngine *engine); + QV4::ReturnedValue v4value() const { return m_v4value.value(); } + + QQuickShapePath path; + + void clear(); + +private: + QV4::PersistentValue m_v4value; +}; + +class QQuickShapeStrokeFillParamsObject : public QObject +{ + Q_OBJECT + +public: + QQuickShapeStrokeFillParamsObject(QObject *parent = nullptr) : QObject(parent) { } + + void setV4Engine(QV4::ExecutionEngine *engine); + QV4::ReturnedValue v4value() const { return m_v4value.value(); } + + QQuickShapeStrokeFillParams sfp; + QV4::PersistentValue v4fillGradient; + + void clear(); + +private: + QV4::PersistentValue m_v4value; +}; + +#ifndef QT_NO_OPENGL + +class QQuickShapeGradientCache : public QOpenGLSharedResource +{ +public: + struct GradientDesc { + QGradientStops stops; + QPointF start; + QPointF end; + QQuickShapeGradient::SpreadMode spread; + bool operator==(const GradientDesc &other) const + { + return start == other.start && end == other.end && spread == other.spread + && stops == other.stops; + } + }; + + QQuickShapeGradientCache(QOpenGLContext *context) : QOpenGLSharedResource(context->shareGroup()) { } + ~QQuickShapeGradientCache(); + + void invalidateResource() override; + void freeResource(QOpenGLContext *) override; + + QSGTexture *get(const GradientDesc &grad); + + static QQuickShapeGradientCache *currentCache(); + +private: + QHash m_cache; +}; + +inline uint qHash(const QQuickShapeGradientCache::GradientDesc &v, uint seed = 0) +{ + uint h = seed; + h += v.start.x() + v.end.y() + v.spread; + for (int i = 0; i < 3 && i < v.stops.count(); ++i) + h += v.stops[i].second.rgba(); + return h; +} + +#endif // QT_NO_OPENGL + +QT_END_NAMESPACE + +#endif diff --git a/src/imports/shapes/qquickshapegenericrenderer.cpp b/src/imports/shapes/qquickshapegenericrenderer.cpp new file mode 100644 index 0000000000..ff226959eb --- /dev/null +++ b/src/imports/shapes/qquickshapegenericrenderer.cpp @@ -0,0 +1,775 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickshapegenericrenderer_p.h" +#include +#include +#include + +#ifndef QT_NO_OPENGL +#include +#include +#include +#include +#endif + +QT_BEGIN_NAMESPACE + +static const qreal TRI_SCALE = 1; + +struct ColoredVertex // must match QSGGeometry::ColoredPoint2D +{ + float x, y; + QQuickShapeGenericRenderer::Color4ub color; + void set(float nx, float ny, QQuickShapeGenericRenderer::Color4ub ncolor) + { + x = nx; y = ny; color = ncolor; + } +}; + +static inline QQuickShapeGenericRenderer::Color4ub colorToColor4ub(const QColor &c) +{ + QQuickShapeGenericRenderer::Color4ub color = { + uchar(qRound(c.redF() * c.alphaF() * 255)), + uchar(qRound(c.greenF() * c.alphaF() * 255)), + uchar(qRound(c.blueF() * c.alphaF() * 255)), + uchar(qRound(c.alphaF() * 255)) + }; + return color; +} + +QQuickShapeGenericStrokeFillNode::QQuickShapeGenericStrokeFillNode(QQuickWindow *window) + : m_geometry(new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0, 0)), + m_window(window), + m_material(nullptr) +{ + setGeometry(m_geometry); + activateMaterial(MatSolidColor); +#ifdef QSG_RUNTIME_DESCRIPTION + qsgnode_set_description(this, QLatin1String("stroke-fill")); +#endif +} + +QQuickShapeGenericStrokeFillNode::~QQuickShapeGenericStrokeFillNode() +{ + delete m_geometry; +} + +void QQuickShapeGenericStrokeFillNode::activateMaterial(Material m) +{ + switch (m) { + case MatSolidColor: + // Use vertexcolor material. Items with different colors remain batchable + // this way, at the expense of having to provide per-vertex color values. + if (!m_solidColorMaterial) + m_solidColorMaterial.reset(QQuickShapeGenericMaterialFactory::createVertexColor(m_window)); + m_material = m_solidColorMaterial.data(); + break; + case MatLinearGradient: + if (!m_linearGradientMaterial) + m_linearGradientMaterial.reset(QQuickShapeGenericMaterialFactory::createLinearGradient(m_window, this)); + m_material = m_linearGradientMaterial.data(); + break; + default: + qWarning("Unknown material %d", m); + return; + } + + if (material() != m_material) + setMaterial(m_material); +} + +static bool q_supportsElementIndexUint(QSGRendererInterface::GraphicsApi api) +{ + static bool elementIndexUint = true; +#ifndef QT_NO_OPENGL + if (api == QSGRendererInterface::OpenGL) { + static bool elementIndexUintChecked = false; + if (!elementIndexUintChecked) { + elementIndexUintChecked = true; + QOpenGLContext *context = QOpenGLContext::currentContext(); + QScopedPointer dummyContext; + QScopedPointer dummySurface; + bool ok = true; + if (!context) { + dummyContext.reset(new QOpenGLContext); + dummyContext->create(); + context = dummyContext.data(); + dummySurface.reset(new QOffscreenSurface); + dummySurface->setFormat(context->format()); + dummySurface->create(); + ok = context->makeCurrent(dummySurface.data()); + } + if (ok) { + elementIndexUint = static_cast(context->functions())->hasOpenGLExtension( + QOpenGLExtensions::ElementIndexUint); + } + } + } +#else + Q_UNUSED(api); +#endif + return elementIndexUint; +} + +QQuickShapeGenericRenderer::~QQuickShapeGenericRenderer() +{ + for (ShapePathData &d : m_sp) { + if (d.pendingFill) + d.pendingFill->orphaned = true; + if (d.pendingStroke) + d.pendingStroke->orphaned = true; + } +} + +// sync, and so triangulation too, happens on the gui thread +// - except when async is set, in which case triangulation is moved to worker threads + +void QQuickShapeGenericRenderer::beginSync(int totalCount) +{ + if (m_sp.count() != totalCount) { + m_sp.resize(totalCount); + m_accDirty |= DirtyList; + } + for (ShapePathData &d : m_sp) + d.syncDirty = 0; +} + +void QQuickShapeGenericRenderer::setPath(int index, const QQuickPath *path) +{ + ShapePathData &d(m_sp[index]); + d.path = path ? path->path() : QPainterPath(); + d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom; +} + +void QQuickShapeGenericRenderer::setJSPath(int index, const QQuickShapePathCommands &path) +{ + ShapePathData &d(m_sp[index]); + d.path = path.toPainterPath(); + d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom; +} + +void QQuickShapeGenericRenderer::setStrokeColor(int index, const QColor &color) +{ + ShapePathData &d(m_sp[index]); + d.strokeColor = colorToColor4ub(color); + d.syncDirty |= DirtyColor; +} + +void QQuickShapeGenericRenderer::setStrokeWidth(int index, qreal w) +{ + ShapePathData &d(m_sp[index]); + d.strokeWidth = w; + if (w >= 0.0f) + d.pen.setWidthF(w); + d.syncDirty |= DirtyStrokeGeom; +} + +void QQuickShapeGenericRenderer::setFillColor(int index, const QColor &color) +{ + ShapePathData &d(m_sp[index]); + d.fillColor = colorToColor4ub(color); + d.syncDirty |= DirtyColor; +} + +void QQuickShapeGenericRenderer::setFillRule(int index, QQuickShapePath::FillRule fillRule) +{ + ShapePathData &d(m_sp[index]); + d.fillRule = Qt::FillRule(fillRule); + d.syncDirty |= DirtyFillGeom; +} + +void QQuickShapeGenericRenderer::setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) +{ + ShapePathData &d(m_sp[index]); + d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); + d.pen.setMiterLimit(miterLimit); + d.syncDirty |= DirtyStrokeGeom; +} + +void QQuickShapeGenericRenderer::setCapStyle(int index, QQuickShapePath::CapStyle capStyle) +{ + ShapePathData &d(m_sp[index]); + d.pen.setCapStyle(Qt::PenCapStyle(capStyle)); + d.syncDirty |= DirtyStrokeGeom; +} + +void QQuickShapeGenericRenderer::setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) +{ + ShapePathData &d(m_sp[index]); + d.pen.setStyle(Qt::PenStyle(strokeStyle)); + if (strokeStyle == QQuickShapePath::DashLine) { + d.pen.setDashPattern(dashPattern); + d.pen.setDashOffset(dashOffset); + } + d.syncDirty |= DirtyStrokeGeom; +} + +void QQuickShapeGenericRenderer::setFillGradient(int index, QQuickShapeGradient *gradient) +{ + ShapePathData &d(m_sp[index]); + d.fillGradientActive = gradient != nullptr; + if (gradient) { + d.fillGradient.stops = gradient->sortedGradientStops(); + d.fillGradient.spread = gradient->spread(); + if (QQuickShapeLinearGradient *g = qobject_cast(gradient)) { + d.fillGradient.start = QPointF(g->x1(), g->y1()); + d.fillGradient.end = QPointF(g->x2(), g->y2()); + } else { + Q_UNREACHABLE(); + } + } + d.syncDirty |= DirtyFillGradient; +} + +void QQuickShapeFillRunnable::run() +{ + QQuickShapeGenericRenderer::triangulateFill(path, fillColor, &fillVertices, &fillIndices, &indexType, supportsElementIndexUint); + emit done(this); +} + +void QQuickShapeStrokeRunnable::run() +{ + QQuickShapeGenericRenderer::triangulateStroke(path, pen, strokeColor, &strokeVertices, clipSize); + emit done(this); +} + +void QQuickShapeGenericRenderer::setAsyncCallback(void (*callback)(void *), void *data) +{ + m_asyncCallback = callback; + m_asyncCallbackData = data; +} + +static QThreadPool *pathWorkThreadPool = nullptr; + +static void deletePathWorkThreadPool() +{ + delete pathWorkThreadPool; + pathWorkThreadPool = nullptr; +} + +void QQuickShapeGenericRenderer::endSync(bool async) +{ + bool didKickOffAsync = false; + + for (int i = 0; i < m_sp.count(); ++i) { + ShapePathData &d(m_sp[i]); + 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; + } + + if (async && !pathWorkThreadPool) { + qAddPostRoutine(deletePathWorkThreadPool); + pathWorkThreadPool = new QThreadPool; + const int idealCount = QThread::idealThreadCount(); + pathWorkThreadPool->setMaxThreadCount(idealCount > 0 ? idealCount * 2 : 4); + } + + if ((d.syncDirty & DirtyFillGeom) && d.fillColor.a) { + d.path.setFillRule(d.fillRule); + if (m_api == QSGRendererInterface::Unknown) + m_api = m_item->window()->rendererInterface()->graphicsApi(); + if (async) { + QQuickShapeFillRunnable *r = new QQuickShapeFillRunnable; + r->setAutoDelete(false); + if (d.pendingFill) + d.pendingFill->orphaned = true; + d.pendingFill = r; + r->path = d.path; + r->fillColor = d.fillColor; + r->supportsElementIndexUint = q_supportsElementIndexUint(m_api); + // Unlikely in practice but in theory m_sp could be + // resized. Therefore, capture 'i' instead of 'd'. + QObject::connect(r, &QQuickShapeFillRunnable::done, qApp, [this, i](QQuickShapeFillRunnable *r) { + // Bail out when orphaned (meaning either another run was + // started after this one, or the renderer got destroyed). + if (!r->orphaned && i < m_sp.count()) { + ShapePathData &d(m_sp[i]); + d.fillVertices = r->fillVertices; + d.fillIndices = r->fillIndices; + d.indexType = r->indexType; + d.pendingFill = nullptr; + d.effectiveDirty |= DirtyFillGeom; + maybeUpdateAsyncItem(); + } + r->deleteLater(); + }); + didKickOffAsync = true; + pathWorkThreadPool->start(r); + } else { + triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices, &d.indexType, q_supportsElementIndexUint(m_api)); + } + } + + if ((d.syncDirty & DirtyStrokeGeom) && d.strokeWidth >= 0.0f && d.strokeColor.a) { + if (async) { + QQuickShapeStrokeRunnable *r = new QQuickShapeStrokeRunnable; + r->setAutoDelete(false); + if (d.pendingStroke) + d.pendingStroke->orphaned = true; + d.pendingStroke = r; + r->path = d.path; + r->pen = d.pen; + r->strokeColor = d.strokeColor; + r->clipSize = QSize(m_item->width(), m_item->height()); + QObject::connect(r, &QQuickShapeStrokeRunnable::done, qApp, [this, i](QQuickShapeStrokeRunnable *r) { + if (!r->orphaned && i < m_sp.count()) { + ShapePathData &d(m_sp[i]); + d.strokeVertices = r->strokeVertices; + d.pendingStroke = nullptr; + d.effectiveDirty |= DirtyStrokeGeom; + maybeUpdateAsyncItem(); + } + r->deleteLater(); + }); + didKickOffAsync = true; + pathWorkThreadPool->start(r); + } else { + triangulateStroke(d.path, d.pen, d.strokeColor, &d.strokeVertices, + QSize(m_item->width(), m_item->height())); + } + } + } + + if (!didKickOffAsync && async && m_asyncCallback) + m_asyncCallback(m_asyncCallbackData); +} + +void QQuickShapeGenericRenderer::maybeUpdateAsyncItem() +{ + for (const ShapePathData &d : qAsConst(m_sp)) { + if (d.pendingFill || d.pendingStroke) + return; + } + m_accDirty |= DirtyFillGeom | DirtyStrokeGeom; + m_item->update(); + if (m_asyncCallback) + m_asyncCallback(m_asyncCallbackData); +} + +// the stroke/fill triangulation functions may be invoked either on the gui +// thread or some worker thread and must thus be self-contained. +void QQuickShapeGenericRenderer::triangulateFill(const QPainterPath &path, + const Color4ub &fillColor, + VertexContainerType *fillVertices, + IndexContainerType *fillIndices, + QSGGeometry::Type *indexType, + bool supportsElementIndexUint) +{ + const QVectorPath &vp = qtVectorPathForPath(path); + + QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(TRI_SCALE, TRI_SCALE), 1, supportsElementIndexUint); + const int vertexCount = ts.vertices.count() / 2; // just a qreal vector with x,y hence the / 2 + fillVertices->resize(vertexCount); + ColoredVertex *vdst = reinterpret_cast(fillVertices->data()); + const qreal *vsrc = ts.vertices.constData(); + for (int i = 0; i < vertexCount; ++i) + vdst[i].set(vsrc[i * 2] / TRI_SCALE, vsrc[i * 2 + 1] / TRI_SCALE, fillColor); + + size_t indexByteSize; + if (ts.indices.type() == QVertexIndexVector::UnsignedShort) { + *indexType = QSGGeometry::UnsignedShortType; + // fillIndices is still QVector. Just resize to N/2 and pack + // the N quint16s into it. + fillIndices->resize(ts.indices.size() / 2); + indexByteSize = ts.indices.size() * sizeof(quint16); + } else { + *indexType = QSGGeometry::UnsignedIntType; + fillIndices->resize(ts.indices.size()); + indexByteSize = ts.indices.size() * sizeof(quint32); + } + memcpy(fillIndices->data(), ts.indices.data(), indexByteSize); +} + +void QQuickShapeGenericRenderer::triangulateStroke(const QPainterPath &path, + const QPen &pen, + const Color4ub &strokeColor, + VertexContainerType *strokeVertices, + const QSize &clipSize) +{ + const QVectorPath &vp = qtVectorPathForPath(path); + const QRectF clip(QPointF(0, 0), clipSize); + const qreal inverseScale = 1.0 / TRI_SCALE; + + QTriangulatingStroker stroker; + stroker.setInvScale(inverseScale); + + if (pen.style() == Qt::SolidLine) { + stroker.process(vp, pen, clip, 0); + } else { + QDashedStrokeProcessor dashStroker; + dashStroker.setInvScale(inverseScale); + dashStroker.process(vp, pen, clip, 0); + QVectorPath dashStroke(dashStroker.points(), dashStroker.elementCount(), + dashStroker.elementTypes(), 0); + stroker.process(dashStroke, pen, clip, 0); + } + + if (!stroker.vertexCount()) { + strokeVertices->clear(); + return; + } + + const int vertexCount = stroker.vertexCount() / 2; // just a float vector with x,y hence the / 2 + strokeVertices->resize(vertexCount); + ColoredVertex *vdst = reinterpret_cast(strokeVertices->data()); + const float *vsrc = stroker.vertices(); + for (int i = 0; i < vertexCount; ++i) + vdst[i].set(vsrc[i * 2], vsrc[i * 2 + 1], strokeColor); +} + +void QQuickShapeGenericRenderer::setRootNode(QQuickShapeGenericNode *node) +{ + if (m_rootNode != node) { + m_rootNode = node; + m_accDirty |= DirtyList; + } +} + +// on the render thread with gui blocked +void QQuickShapeGenericRenderer::updateNode() +{ + if (!m_rootNode || !m_accDirty) + return; + +// [ m_rootNode ] +// / / / +// #0 [ fill ] [ stroke ] [ next ] +// / / | +// #1 [ fill ] [ stroke ] [ next ] +// / / | +// #2 [ fill ] [ stroke ] [ next ] +// ... +// ... + + QQuickShapeGenericNode **nodePtr = &m_rootNode; + QQuickShapeGenericNode *prevNode = nullptr; + + for (ShapePathData &d : m_sp) { + if (!*nodePtr) { + *nodePtr = new QQuickShapeGenericNode; + prevNode->m_next = *nodePtr; + prevNode->appendChildNode(*nodePtr); + } + + QQuickShapeGenericNode *node = *nodePtr; + + if (m_accDirty & DirtyList) + d.effectiveDirty |= DirtyFillGeom | DirtyStrokeGeom | DirtyColor | DirtyFillGradient; + + if (!d.effectiveDirty) { + prevNode = node; + nodePtr = &node->m_next; + continue; + } + + if (d.fillColor.a == 0) { + delete node->m_fillNode; + node->m_fillNode = nullptr; + } else if (!node->m_fillNode) { + node->m_fillNode = new QQuickShapeGenericStrokeFillNode(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 |= DirtyFillGeom; + } + + 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 QQuickShapeGenericStrokeFillNode(m_item->window()); + node->appendChildNode(node->m_strokeNode); + d.effectiveDirty |= DirtyStrokeGeom; + } + + updateFillNode(&d, node); + updateStrokeNode(&d, node); + + d.effectiveDirty = 0; + + prevNode = node; + nodePtr = &node->m_next; + } + + if (*nodePtr && prevNode) { + prevNode->removeChildNode(*nodePtr); + delete *nodePtr; + *nodePtr = nullptr; + } + + m_accDirty = 0; +} + +void QQuickShapeGenericRenderer::updateShadowDataInNode(ShapePathData *d, QQuickShapeGenericStrokeFillNode *n) +{ + if (d->fillGradientActive) { + if (d->effectiveDirty & DirtyFillGradient) + n->m_fillGradient = d->fillGradient; + } +} + +void QQuickShapeGenericRenderer::updateFillNode(ShapePathData *d, QQuickShapeGenericNode *node) +{ + if (!node->m_fillNode) + return; + if (!(d->effectiveDirty & (DirtyFillGeom | DirtyColor | DirtyFillGradient))) + return; + + // Make a copy of the data that will be accessed by the material on + // the render thread. This must be done even when we bail out below. + QQuickShapeGenericStrokeFillNode *n = node->m_fillNode; + updateShadowDataInNode(d, n); + + QSGGeometry *g = n->m_geometry; + if (d->fillVertices.isEmpty()) { + if (g->vertexCount() || g->indexCount()) { + g->allocate(0, 0); + n->markDirty(QSGNode::DirtyGeometry); + } + return; + } + + if (d->fillGradientActive) { + n->activateMaterial(QQuickShapeGenericStrokeFillNode::MatLinearGradient); + if (d->effectiveDirty & DirtyFillGradient) { + // 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 (!(d->effectiveDirty & DirtyFillGeom)) + return; + } + } else { + n->activateMaterial(QQuickShapeGenericStrokeFillNode::MatSolidColor); + // fast path for updating only color values when no change in vertex positions + if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyFillGeom)) { + ColoredVertex *vdst = reinterpret_cast(g->vertexData()); + for (int i = 0; i < g->vertexCount(); ++i) + vdst[i].set(vdst[i].x, vdst[i].y, d->fillColor); + n->markDirty(QSGNode::DirtyGeometry); + return; + } + } + + const int indexCount = d->indexType == QSGGeometry::UnsignedShortType + ? d->fillIndices.count() * 2 : d->fillIndices.count(); + if (g->indexType() != d->indexType) { + g = new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), + d->fillVertices.count(), indexCount, d->indexType); + n->setGeometry(g); + delete n->m_geometry; + n->m_geometry = g; + } else { + g->allocate(d->fillVertices.count(), indexCount); + } + g->setDrawingMode(QSGGeometry::DrawTriangles); + 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 QQuickShapeGenericRenderer::updateStrokeNode(ShapePathData *d, QQuickShapeGenericNode *node) +{ + if (!node->m_strokeNode) + return; + if (!(d->effectiveDirty & (DirtyStrokeGeom | DirtyColor))) + return; + + QQuickShapeGenericStrokeFillNode *n = node->m_strokeNode; + QSGGeometry *g = n->m_geometry; + if (d->strokeVertices.isEmpty()) { + if (g->vertexCount() || g->indexCount()) { + g->allocate(0, 0); + n->markDirty(QSGNode::DirtyGeometry); + } + return; + } + + n->markDirty(QSGNode::DirtyGeometry); + + // Async loading runs update once, bails out above, then updates again once + // ready. Set the material dirty then. This is in-line with fill where the + // first activateMaterial() achieves the same. + if (!g->vertexCount()) + n->markDirty(QSGNode::DirtyMaterial); + + if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyStrokeGeom)) { + ColoredVertex *vdst = reinterpret_cast(g->vertexData()); + for (int i = 0; i < g->vertexCount(); ++i) + vdst[i].set(vdst[i].x, vdst[i].y, d->strokeColor); + return; + } + + g->allocate(d->strokeVertices.count(), 0); + g->setDrawingMode(QSGGeometry::DrawTriangleStrip); + memcpy(g->vertexData(), d->strokeVertices.constData(), g->vertexCount() * g->sizeOfVertex()); +} + +QSGMaterial *QQuickShapeGenericMaterialFactory::createVertexColor(QQuickWindow *window) +{ + QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); + +#ifndef QT_NO_OPENGL + if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... + return new QSGVertexColorMaterial; +#endif + + qWarning("Vertex-color material: Unsupported graphics API %d", api); + return nullptr; +} + +QSGMaterial *QQuickShapeGenericMaterialFactory::createLinearGradient(QQuickWindow *window, + QQuickShapeGenericStrokeFillNode *node) +{ + QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); + +#ifndef QT_NO_OPENGL + if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... + return new QQuickShapeLinearGradientMaterial(node); +#endif + + qWarning("Linear gradient material: Unsupported graphics API %d", api); + return nullptr; +} + +#ifndef QT_NO_OPENGL + +QSGMaterialType QQuickShapeLinearGradientShader::type; + +QQuickShapeLinearGradientShader::QQuickShapeLinearGradientShader() +{ + setShaderSourceFile(QOpenGLShader::Vertex, + QStringLiteral(":/qt-project.org/items/shaders/lineargradient.vert")); + setShaderSourceFile(QOpenGLShader::Fragment, + QStringLiteral(":/qt-project.org/items/shaders/lineargradient.frag")); +} + +void QQuickShapeLinearGradientShader::initialize() +{ + m_opacityLoc = program()->uniformLocation("opacity"); + m_matrixLoc = program()->uniformLocation("matrix"); + m_gradStartLoc = program()->uniformLocation("gradStart"); + m_gradEndLoc = program()->uniformLocation("gradEnd"); +} + +void QQuickShapeLinearGradientShader::updateState(const RenderState &state, QSGMaterial *mat, QSGMaterial *) +{ + QQuickShapeLinearGradientMaterial *m = static_cast(mat); + + if (state.isOpacityDirty()) + program()->setUniformValue(m_opacityLoc, state.opacity()); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); + + QQuickShapeGenericStrokeFillNode *node = m->node(); + program()->setUniformValue(m_gradStartLoc, QVector2D(node->m_fillGradient.start)); + program()->setUniformValue(m_gradEndLoc, QVector2D(node->m_fillGradient.end)); + + QSGTexture *tx = QQuickShapeGradientCache::currentCache()->get(node->m_fillGradient); + tx->bind(); +} + +char const *const *QQuickShapeLinearGradientShader::attributeNames() const +{ + static const char *const attr[] = { "vertexCoord", "vertexColor", nullptr }; + return attr; +} + +int QQuickShapeLinearGradientMaterial::compare(const QSGMaterial *other) const +{ + Q_ASSERT(other && type() == other->type()); + const QQuickShapeLinearGradientMaterial *m = static_cast(other); + + QQuickShapeGenericStrokeFillNode *a = node(); + QQuickShapeGenericStrokeFillNode *b = m->node(); + Q_ASSERT(a && b); + if (a == b) + return 0; + + const QQuickShapeGradientCache::GradientDesc *ga = &a->m_fillGradient; + const QQuickShapeGradientCache::GradientDesc *gb = &b->m_fillGradient; + + if (int d = ga->spread - gb->spread) + return d; + + if (int d = ga->start.x() - gb->start.x()) + return d; + if (int d = ga->start.y() - gb->start.y()) + return d; + if (int d = ga->end.x() - gb->end.x()) + return d; + if (int d = ga->end.y() - gb->end.y()) + return d; + + if (int d = ga->stops.count() - gb->stops.count()) + return d; + + for (int i = 0; i < ga->stops.count(); ++i) { + if (int d = ga->stops[i].first - gb->stops[i].first) + return d; + if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba()) + return d; + } + + return 0; +} + +#endif // QT_NO_OPENGL + +QT_END_NAMESPACE diff --git a/src/imports/shapes/qquickshapegenericrenderer_p.h b/src/imports/shapes/qquickshapegenericrenderer_p.h new file mode 100644 index 0000000000..ba50f50309 --- /dev/null +++ b/src/imports/shapes/qquickshapegenericrenderer_p.h @@ -0,0 +1,303 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSHAPEGENERICRENDERER_P_H +#define QQUICKSHAPEGENERICRENDERER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickshape_p_p.h" +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickShapeGenericNode; +class QQuickShapeGenericStrokeFillNode; +class QQuickShapeFillRunnable; +class QQuickShapeStrokeRunnable; + +class QQuickShapeGenericRenderer : public QQuickAbstractPathRenderer +{ +public: + enum Dirty { + DirtyFillGeom = 0x01, + DirtyStrokeGeom = 0x02, + DirtyColor = 0x04, + DirtyFillGradient = 0x08, + DirtyList = 0x10 // only for accDirty + }; + + QQuickShapeGenericRenderer(QQuickItem *item) + : m_item(item), + m_api(QSGRendererInterface::Unknown), + m_rootNode(nullptr), + m_accDirty(0), + m_asyncCallback(nullptr) + { } + ~QQuickShapeGenericRenderer(); + + void beginSync(int totalCount) override; + void setPath(int index, const QQuickPath *path) override; + void setJSPath(int index, const QQuickShapePathCommands &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, QQuickShapePath::FillRule fillRule) override; + void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) override; + void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) override; + void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) override; + void setFillGradient(int index, QQuickShapeGradient *gradient) override; + void endSync(bool async) override; + void setAsyncCallback(void (*)(void *), void *) override; + Flags flags() const override { return SupportsAsync; } + + void updateNode() override; + + void setRootNode(QQuickShapeGenericNode *node); + + struct Color4ub { unsigned char r, g, b, a; }; + typedef QVector VertexContainerType; + typedef QVector IndexContainerType; + + static void triangulateFill(const QPainterPath &path, + const Color4ub &fillColor, + VertexContainerType *fillVertices, + IndexContainerType *fillIndices, + QSGGeometry::Type *indexType, + bool supportsElementIndexUint); + static void triangulateStroke(const QPainterPath &path, + const QPen &pen, + const Color4ub &strokeColor, + VertexContainerType *strokeVertices, + const QSize &clipSize); + +private: + void maybeUpdateAsyncItem(); + + struct ShapePathData { + float strokeWidth; + QPen pen; + Color4ub strokeColor; + Color4ub fillColor; + Qt::FillRule fillRule; + QPainterPath path; + bool fillGradientActive; + QQuickShapeGradientCache::GradientDesc fillGradient; + VertexContainerType fillVertices; + IndexContainerType fillIndices; + QSGGeometry::Type indexType; + VertexContainerType strokeVertices; + int syncDirty; + int effectiveDirty = 0; + QQuickShapeFillRunnable *pendingFill = nullptr; + QQuickShapeStrokeRunnable *pendingStroke = nullptr; + }; + + void updateShadowDataInNode(ShapePathData *d, QQuickShapeGenericStrokeFillNode *n); + void updateFillNode(ShapePathData *d, QQuickShapeGenericNode *node); + void updateStrokeNode(ShapePathData *d, QQuickShapeGenericNode *node); + + QQuickItem *m_item; + QSGRendererInterface::GraphicsApi m_api; + QQuickShapeGenericNode *m_rootNode; + QVector m_sp; + int m_accDirty; + void (*m_asyncCallback)(void *); + void *m_asyncCallbackData; +}; + +class QQuickShapeFillRunnable : public QObject, public QRunnable +{ + Q_OBJECT + +public: + void run() override; + + bool orphaned = false; + + // input + QPainterPath path; + QQuickShapeGenericRenderer::Color4ub fillColor; + bool supportsElementIndexUint; + + // output + QQuickShapeGenericRenderer::VertexContainerType fillVertices; + QQuickShapeGenericRenderer::IndexContainerType fillIndices; + QSGGeometry::Type indexType; + +Q_SIGNALS: + void done(QQuickShapeFillRunnable *self); +}; + +class QQuickShapeStrokeRunnable : public QObject, public QRunnable +{ + Q_OBJECT + +public: + void run() override; + + bool orphaned = false; + + // input + QPainterPath path; + QPen pen; + QQuickShapeGenericRenderer::Color4ub strokeColor; + QSize clipSize; + + // output + QQuickShapeGenericRenderer::VertexContainerType strokeVertices; + +Q_SIGNALS: + void done(QQuickShapeStrokeRunnable *self); +}; + +class QQuickShapeGenericStrokeFillNode : public QSGGeometryNode +{ +public: + QQuickShapeGenericStrokeFillNode(QQuickWindow *window); + ~QQuickShapeGenericStrokeFillNode(); + + enum Material { + MatSolidColor, + MatLinearGradient + }; + + void activateMaterial(Material m); + + QQuickWindow *window() const { return m_window; } + + // shadow data for custom materials + QQuickShapeGradientCache::GradientDesc m_fillGradient; + +private: + QSGGeometry *m_geometry; + QQuickWindow *m_window; + QSGMaterial *m_material; + QScopedPointer m_solidColorMaterial; + QScopedPointer m_linearGradientMaterial; + + friend class QQuickShapeGenericRenderer; +}; + +class QQuickShapeGenericNode : public QSGNode +{ +public: + QQuickShapeGenericStrokeFillNode *m_fillNode = nullptr; + QQuickShapeGenericStrokeFillNode *m_strokeNode = nullptr; + QQuickShapeGenericNode *m_next = nullptr; +}; + +class QQuickShapeGenericMaterialFactory +{ +public: + static QSGMaterial *createVertexColor(QQuickWindow *window); + static QSGMaterial *createLinearGradient(QQuickWindow *window, QQuickShapeGenericStrokeFillNode *node); +}; + +#ifndef QT_NO_OPENGL + +class QQuickShapeLinearGradientShader : public QSGMaterialShader +{ +public: + QQuickShapeLinearGradientShader(); + + void initialize() override; + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; + char const *const *attributeNames() const override; + + static QSGMaterialType type; + +private: + int m_opacityLoc; + int m_matrixLoc; + int m_gradStartLoc; + int m_gradEndLoc; +}; + +class QQuickShapeLinearGradientMaterial : public QSGMaterial +{ +public: + QQuickShapeLinearGradientMaterial(QQuickShapeGenericStrokeFillNode *node) + : m_node(node) + { + // Passing RequiresFullMatrix is essential in order to prevent the + // batch renderer from baking in simple, translate-only transforms into + // the vertex data. The shader will rely on the fact that + // vertexCoord.xy is the Shape-space coordinate and so no modifications + // are welcome. + setFlag(Blending | RequiresFullMatrix); + } + + QSGMaterialType *type() const override + { + return &QQuickShapeLinearGradientShader::type; + } + + int compare(const QSGMaterial *other) const override; + + QSGMaterialShader *createShader() const override + { + return new QQuickShapeLinearGradientShader; + } + + QQuickShapeGenericStrokeFillNode *node() const { return m_node; } + +private: + QQuickShapeGenericStrokeFillNode *m_node; +}; + +#endif // QT_NO_OPENGL + +QT_END_NAMESPACE + +#endif // QQUICKSHAPEGENERICRENDERER_P_H diff --git a/src/imports/shapes/qquickshapenvprrenderer.cpp b/src/imports/shapes/qquickshapenvprrenderer.cpp new file mode 100644 index 0000000000..a3e9d31be5 --- /dev/null +++ b/src/imports/shapes/qquickshapenvprrenderer.cpp @@ -0,0 +1,923 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickshapenvprrenderer_p.h" +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +void QQuickShapeNvprRenderer::beginSync(int totalCount) +{ + if (m_sp.count() != totalCount) { + m_sp.resize(totalCount); + m_accDirty |= DirtyList; + } +} + +void QQuickShapeNvprRenderer::setPath(int index, const QQuickPath *path) +{ + ShapePathGuiData &d(m_sp[index]); + convertPath(path, &d); + d.dirty |= DirtyPath; + m_accDirty |= DirtyPath; +} + +void QQuickShapeNvprRenderer::setJSPath(int index, const QQuickShapePathCommands &path) +{ + ShapePathGuiData &d(m_sp[index]); + convertJSPath(path, &d); + d.dirty |= DirtyPath; + m_accDirty |= DirtyPath; +} + +void QQuickShapeNvprRenderer::setStrokeColor(int index, const QColor &color) +{ + ShapePathGuiData &d(m_sp[index]); + d.strokeColor = color; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; +} + +void QQuickShapeNvprRenderer::setStrokeWidth(int index, qreal w) +{ + ShapePathGuiData &d(m_sp[index]); + d.strokeWidth = w; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; +} + +void QQuickShapeNvprRenderer::setFillColor(int index, const QColor &color) +{ + ShapePathGuiData &d(m_sp[index]); + d.fillColor = color; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; +} + +void QQuickShapeNvprRenderer::setFillRule(int index, QQuickShapePath::FillRule fillRule) +{ + ShapePathGuiData &d(m_sp[index]); + d.fillRule = fillRule; + d.dirty |= DirtyFillRule; + m_accDirty |= DirtyFillRule; +} + +void QQuickShapeNvprRenderer::setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) +{ + ShapePathGuiData &d(m_sp[index]); + d.joinStyle = joinStyle; + d.miterLimit = miterLimit; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; +} + +void QQuickShapeNvprRenderer::setCapStyle(int index, QQuickShapePath::CapStyle capStyle) +{ + ShapePathGuiData &d(m_sp[index]); + d.capStyle = capStyle; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; +} + +void QQuickShapeNvprRenderer::setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) +{ + ShapePathGuiData &d(m_sp[index]); + d.dashActive = strokeStyle == QQuickShapePath::DashLine; + d.dashOffset = dashOffset; + d.dashPattern = dashPattern; + d.dirty |= DirtyDash; + m_accDirty |= DirtyDash; +} + +void QQuickShapeNvprRenderer::setFillGradient(int index, QQuickShapeGradient *gradient) +{ + ShapePathGuiData &d(m_sp[index]); + d.fillGradientActive = gradient != nullptr; + if (gradient) { + d.fillGradient.stops = gradient->sortedGradientStops(); + d.fillGradient.spread = gradient->spread(); + if (QQuickShapeLinearGradient *g = qobject_cast(gradient)) { + d.fillGradient.start = QPointF(g->x1(), g->y1()); + d.fillGradient.end = QPointF(g->x2(), g->y2()); + } else { + Q_UNREACHABLE(); + } + } + d.dirty |= DirtyFillGradient; + m_accDirty |= DirtyFillGradient; +} + +void QQuickShapeNvprRenderer::endSync(bool) +{ +} + +void QQuickShapeNvprRenderer::setNode(QQuickShapeNvprRenderNode *node) +{ + if (m_node != node) { + m_node = node; + m_accDirty |= DirtyList; + } +} + +QDebug operator<<(QDebug debug, const QQuickShapeNvprRenderer::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) { + static struct { GLubyte cmd; const char *s; int coordCount; } nameTab[] = { + { GL_MOVE_TO_NV, "moveTo", 2 }, + { GL_LINE_TO_NV, "lineTo", 2 }, + { GL_QUADRATIC_CURVE_TO_NV, "quadTo", 4 }, + { GL_CUBIC_CURVE_TO_NV, "cubicTo", 6 }, + { GL_LARGE_CW_ARC_TO_NV, "arcTo-large-CW", 5 }, + { GL_LARGE_CCW_ARC_TO_NV, "arcTo-large-CCW", 5 }, + { GL_SMALL_CW_ARC_TO_NV, "arcTo-small-CW", 5 }, + { GL_SMALL_CCW_ARC_TO_NV, "arcTo-small-CCW", 5 }, + { GL_CLOSE_PATH_NV, "closePath", 0 } }; + for (size_t i = 0; i < sizeof(nameTab) / sizeof(nameTab[0]); ++i) { + if (nameTab[i].cmd == cmd) { + QByteArray cs; + for (int j = 0; j < nameTab[i].coordCount; ++j) { + cs.append(QByteArray::number(path.coord[ci++])); + cs.append(' '); + } + debug << "\n " << nameTab[i].s << " " << cs; + break; + } + } + } + return debug; +} + +static inline void appendCoords(QVector *v, QQuickCurve *c, QPointF *pos) +{ + QPointF p(c->hasRelativeX() ? pos->x() + c->relativeX() : c->x(), + c->hasRelativeY() ? pos->y() + c->relativeY() : c->y()); + v->append(p.x()); + v->append(p.y()); + *pos = p; +} + +static inline void appendControlCoords(QVector *v, QQuickPathQuad *c, const QPointF &pos) +{ + QPointF p(c->hasRelativeControlX() ? pos.x() + c->relativeControlX() : c->controlX(), + c->hasRelativeControlY() ? pos.y() + c->relativeControlY() : c->controlY()); + v->append(p.x()); + v->append(p.y()); +} + +static inline void appendControl1Coords(QVector *v, QQuickPathCubic *c, const QPointF &pos) +{ + QPointF p(c->hasRelativeControl1X() ? pos.x() + c->relativeControl1X() : c->control1X(), + c->hasRelativeControl1Y() ? pos.y() + c->relativeControl1Y() : c->control1Y()); + v->append(p.x()); + v->append(p.y()); +} + +static inline void appendControl2Coords(QVector *v, QQuickPathCubic *c, const QPointF &pos) +{ + QPointF p(c->hasRelativeControl2X() ? pos.x() + c->relativeControl2X() : c->control2X(), + c->hasRelativeControl2Y() ? pos.y() + c->relativeControl2Y() : c->control2Y()); + v->append(p.x()); + v->append(p.y()); +} + +void QQuickShapeNvprRenderer::convertPath(const QQuickPath *path, ShapePathGuiData *d) +{ + d->path = NvprPath(); + if (!path) + return; + + const QList &pp(QQuickPathPrivate::get(path)->_pathElements); + if (pp.isEmpty()) + return; + + 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(e)) { + d->path.cmd.append(GL_MOVE_TO_NV); + appendCoords(&d->path.coord, o, &pos); + startPos = pos; + } else if (QQuickPathLine *o = qobject_cast(e)) { + d->path.cmd.append(GL_LINE_TO_NV); + appendCoords(&d->path.coord, o, &pos); + } else if (QQuickPathQuad *o = qobject_cast(e)) { + 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(e)) { + 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(e)) { + const bool sweepFlag = o->direction() == QQuickPathArc::Clockwise; // maps to CCW, not a typo + GLenum cmd; + if (o->useLargeArc()) + cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV; + else + cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV; + 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(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() << "Shape/NVPR: unsupported Path element" << e; + } + } + + // 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); +} + +void QQuickShapeNvprRenderer::convertJSPath(const QQuickShapePathCommands &path, ShapePathGuiData *d) +{ + d->path = NvprPath(); + if (path.cmd.isEmpty()) + return; + + QPointF startPos(0, 0); + QPointF pos(startPos); + int coordIdx = 0; + + for (QQuickShapePathCommands::Command cmd : path.cmd) { + switch (cmd) { + case QQuickShapePathCommands::MoveTo: + d->path.cmd.append(GL_MOVE_TO_NV); + pos = QPointF(path.coords[coordIdx], path.coords[coordIdx + 1]); + startPos = pos; + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 2; + break; + case QQuickShapePathCommands::LineTo: + d->path.cmd.append(GL_LINE_TO_NV); + pos = QPointF(path.coords[coordIdx], path.coords[coordIdx + 1]); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 2; + break; + case QQuickShapePathCommands::QuadTo: + d->path.cmd.append(GL_QUADRATIC_CURVE_TO_NV); + d->path.coord.append(path.coords[coordIdx]); + d->path.coord.append(path.coords[coordIdx + 1]); + pos = QPointF(path.coords[coordIdx + 2], path.coords[coordIdx + 3]); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 4; + break; + case QQuickShapePathCommands::CubicTo: + d->path.cmd.append(GL_CUBIC_CURVE_TO_NV); + d->path.coord.append(path.coords[coordIdx]); + d->path.coord.append(path.coords[coordIdx + 1]); + d->path.coord.append(path.coords[coordIdx + 2]); + d->path.coord.append(path.coords[coordIdx + 3]); + pos = QPointF(path.coords[coordIdx + 4], path.coords[coordIdx + 5]); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 6; + break; + case QQuickShapePathCommands::ArcTo: + { + const bool sweepFlag = !qFuzzyIsNull(path.coords[coordIdx + 5]); + const bool useLargeArc = !qFuzzyIsNull(path.coords[coordIdx + 6]); + GLenum cmd; + if (useLargeArc) + cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV; + else + cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV; + d->path.cmd.append(cmd); + d->path.coord.append(path.coords[coordIdx]); // rx + d->path.coord.append(path.coords[coordIdx + 1]); // ry + d->path.coord.append(path.coords[coordIdx + 2]); // xrot + pos = QPointF(path.coords[coordIdx + 3], path.coords[coordIdx + 4]); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 7; + } + break; + default: + qWarning("Unknown JS path command: %d", cmd); + break; + } + } + + if (pos == startPos) + d->path.cmd.append(GL_CLOSE_PATH_NV); +} + +static inline QVector4D qsg_premultiply(const QColor &c, float globalOpacity) +{ + const float o = c.alphaF() * globalOpacity; + return QVector4D(c.redF() * o, c.greenF() * o, c.blueF() * o, o); +} + +void QQuickShapeNvprRenderer::updateNode() +{ + // Called on the render thread with gui blocked -> update the node with its + // own copy of all relevant data. + + if (!m_accDirty) + return; + + const int count = m_sp.count(); + const bool listChanged = m_accDirty & DirtyList; + if (listChanged) + m_node->m_sp.resize(count); + + for (int i = 0; i < count; ++i) { + ShapePathGuiData &src(m_sp[i]); + QQuickShapeNvprRenderNode::ShapePathRenderData &dst(m_node->m_sp[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 QQuickShapePath::MiterJoin: + dst.joinStyle = GL_MITER_TRUNCATE_NV; + break; + case QQuickShapePath::BevelJoin: + dst.joinStyle = GL_BEVEL_NV; + break; + case QQuickShapePath::RoundJoin: + dst.joinStyle = GL_ROUND_NV; + break; + default: + Q_UNREACHABLE(); + } + dst.miterLimit = src.miterLimit; + switch (src.capStyle) { + case QQuickShapePath::FlatCap: + dst.capStyle = GL_FLAT; + break; + case QQuickShapePath::SquareCap: + dst.capStyle = GL_SQUARE_NV; + break; + case QQuickShapePath::RoundCap: + dst.capStyle = GL_ROUND_NV; + break; + default: + Q_UNREACHABLE(); + } + } + + if (dirty & DirtyFillRule) { + switch (src.fillRule) { + case QQuickShapePath::OddEvenFill: + dst.fillRule = GL_INVERT; + break; + case QQuickShapePath::WindingFill: + dst.fillRule = GL_COUNT_UP_NV; + break; + default: + Q_UNREACHABLE(); + } + } + + if (dirty & DirtyDash) { + dst.dashOffset = src.dashOffset; + if (src.dashActive) { + dst.dashPattern.resize(src.dashPattern.count()); + // Multiply by strokeWidth because the Shape 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 (dirty & DirtyFillGradient) { + dst.fillGradientActive = src.fillGradientActive; + if (src.fillGradientActive) + dst.fillGradient = src.fillGradient; + } + } + + m_node->markDirty(QSGNode::DirtyMaterial); + m_accDirty = 0; +} + +bool QQuickShapeNvprRenderNode::nvprInited = false; +QQuickNvprFunctions QQuickShapeNvprRenderNode::nvpr; +QQuickNvprMaterialManager QQuickShapeNvprRenderNode::mtlmgr; + +QQuickShapeNvprRenderNode::~QQuickShapeNvprRenderNode() +{ + releaseResources(); +} + +void QQuickShapeNvprRenderNode::releaseResources() +{ + for (ShapePathRenderData &d : m_sp) { + if (d.path) { + nvpr.deletePaths(d.path, 1); + d.path = 0; + } + if (d.fallbackFbo) { + delete d.fallbackFbo; + d.fallbackFbo = nullptr; + } + } + + m_fallbackBlitter.destroy(); +} + +void QQuickNvprMaterialManager::create(QQuickNvprFunctions *nvpr) +{ + m_nvpr = nvpr; +} + +void QQuickNvprMaterialManager::releaseResources() +{ + QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); + for (MaterialDesc &mtl : m_materials) { + if (mtl.ppl) { + f->glDeleteProgramPipelines(1, &mtl.ppl); + mtl = MaterialDesc(); + } + } +} + +QQuickNvprMaterialManager::MaterialDesc *QQuickNvprMaterialManager::activateMaterial(Material m) +{ + QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); + MaterialDesc &mtl(m_materials[m]); + + if (!mtl.ppl) { + if (m == MatSolid) { + static const char *fragSrc = + "#version 310 es\n" + "precision highp float;\n" + "out vec4 fragColor;\n" + "uniform vec4 color;\n" + "uniform float opacity;\n" + "void main() {\n" + " fragColor = color * opacity;\n" + "}\n"; + if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { + qWarning("NVPR: Failed to create shader pipeline for solid fill"); + return nullptr; + } + Q_ASSERT(mtl.ppl && mtl.prg); + mtl.uniLoc[0] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "color"); + Q_ASSERT(mtl.uniLoc[0] >= 0); + mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); + Q_ASSERT(mtl.uniLoc[1] >= 0); + } else if (m == MatLinearGradient) { + static const char *fragSrc = + "#version 310 es\n" + "precision highp float;\n" + "layout(location = 0) in vec2 uv;" + "uniform float opacity;\n" + "uniform sampler2D gradTab;\n" + "uniform vec2 gradStart;\n" + "uniform vec2 gradEnd;\n" + "out vec4 fragColor;\n" + "void main() {\n" + " vec2 gradVec = gradEnd - gradStart;\n" + " float gradTabIndex = dot(gradVec, uv - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y);\n" + " fragColor = texture(gradTab, vec2(gradTabIndex, 0.5)) * opacity;\n" + "}\n"; + if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { + qWarning("NVPR: Failed to create shader pipeline for linear gradient"); + return nullptr; + } + Q_ASSERT(mtl.ppl && mtl.prg); + mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); + Q_ASSERT(mtl.uniLoc[1] >= 0); + mtl.uniLoc[2] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradStart"); + Q_ASSERT(mtl.uniLoc[2] >= 0); + mtl.uniLoc[3] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradEnd"); + Q_ASSERT(mtl.uniLoc[3] >= 0); + } else { + Q_UNREACHABLE(); + } + } + + f->glBindProgramPipeline(mtl.ppl); + + return &mtl; +} + +void QQuickShapeNvprRenderNode::updatePath(ShapePathRenderData *d) +{ + if (d->dirty & QQuickShapeNvprRenderer::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()); + } + } + + if (d->dirty & QQuickShapeNvprRenderer::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 (d->dirty & QQuickShapeNvprRenderer::DirtyDash) { + nvpr.pathParameterf(d->path, GL_PATH_DASH_OFFSET_NV, d->dashOffset); + // count == 0 -> no dash + nvpr.pathDashArray(d->path, d->dashPattern.count(), d->dashPattern.constData()); + } + + if (d->dirty) + d->fallbackValid = false; +} + +void QQuickShapeNvprRenderNode::renderStroke(ShapePathRenderData *d, int strokeStencilValue, int writeMask) +{ + QQuickNvprMaterialManager::MaterialDesc *mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); + f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], + d->strokeColor.x(), d->strokeColor.y(), d->strokeColor.z(), d->strokeColor.w()); + f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); + + nvpr.stencilThenCoverStrokePath(d->path, strokeStencilValue, writeMask, GL_CONVEX_HULL_NV); +} + +void QQuickShapeNvprRenderNode::renderFill(ShapePathRenderData *d) +{ + QQuickNvprMaterialManager::MaterialDesc *mtl = nullptr; + if (d->fillGradientActive) { + mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatLinearGradient); + QSGTexture *tx = QQuickShapeGradientCache::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 + // we need since the gradient's start and stop are in that space too. + GLfloat coeff[6] = { 1, 0, 0, + 0, 1, 0 }; + nvpr.programPathFragmentInputGen(mtl->prg, 0, GL_OBJECT_LINEAR_NV, 2, coeff); + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[2], 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], + 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(d->path, d->fillRule, writeMask, GL_BOUNDING_BOX_NV); +} + +void QQuickShapeNvprRenderNode::renderOffscreenFill(ShapePathRenderData *d) +{ + if (d->fallbackValid && d->fallbackFbo) + return; + + GLfloat bb[4]; + nvpr.getPathParameterfv(d->path, GL_PATH_STROKE_BOUNDING_BOX_NV, bb); + QSize sz = QSizeF(bb[2] - bb[0] + 1, bb[3] - bb[1] + 1).toSize(); + d->fallbackSize = QSize(qMax(32, sz.width()), qMax(32, sz.height())); + d->fallbackTopLeft = QPointF(bb[0], bb[1]); + + if (d->fallbackFbo && d->fallbackFbo->size() != d->fallbackSize) { + delete d->fallbackFbo; + d->fallbackFbo = nullptr; + } + if (!d->fallbackFbo) + d->fallbackFbo = new QOpenGLFramebufferObject(d->fallbackSize, QOpenGLFramebufferObject::CombinedDepthStencil); + if (!d->fallbackFbo->bind()) + return; + + GLint prevViewport[4]; + f->glGetIntegerv(GL_VIEWPORT, prevViewport); + + f->glViewport(0, 0, d->fallbackSize.width(), d->fallbackSize.height()); + f->glDisable(GL_DEPTH_TEST); + f->glClearColor(0, 0, 0, 0); + f->glClearStencil(0); + f->glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); + f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + + QMatrix4x4 mv; + mv.translate(-d->fallbackTopLeft.x(), -d->fallbackTopLeft.y()); + nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, mv.constData()); + QMatrix4x4 proj; + proj.ortho(0, d->fallbackSize.width(), d->fallbackSize.height(), 0, 1, -1); + nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, proj.constData()); + + renderFill(d); + + d->fallbackFbo->release(); + f->glEnable(GL_DEPTH_TEST); + f->glViewport(prevViewport[0], prevViewport[1], prevViewport[2], prevViewport[3]); + + d->fallbackValid = true; +} + +void QQuickShapeNvprRenderNode::setupStencilForCover(bool stencilClip, int sv) +{ + if (!stencilClip) { + // Assume stencil buffer is cleared to 0 for each frame. + // Within the frame dppass=GL_ZERO for glStencilOp ensures stencil is reset and so no need to clear. + f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); + f->glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO); + } else { + f->glStencilFunc(GL_LESS, sv, 0xFF); // pass if (sv & 0xFF) < (stencil_value & 0xFF) + f->glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); // dppass: replace with the original value (clip's stencil ref value) + } +} + +void QQuickShapeNvprRenderNode::render(const RenderState *state) +{ + f = QOpenGLContext::currentContext()->extraFunctions(); + + if (!nvprInited) { + if (!nvpr.create()) { + qWarning("NVPR init failed"); + return; + } + mtlmgr.create(&nvpr); + nvprInited = true; + } + + f->glUseProgram(0); + f->glStencilMask(~0); + f->glEnable(GL_STENCIL_TEST); + + const bool stencilClip = state->stencilEnabled(); + // when true, the stencil buffer already has a clip path with a ref value of sv + const int sv = state->stencilValue(); + const bool hasScissor = state->scissorEnabled(); + + if (hasScissor) { + // scissor rect is already set, just enable scissoring + f->glEnable(GL_SCISSOR_TEST); + } + + // 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); + + bool reloadMatrices = true; + + for (ShapePathRenderData &d : m_sp) { + updatePath(&d); + + const bool hasFill = d.hasFill(); + const bool hasStroke = d.hasStroke(); + + 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... + if (hasScissor) + f->glDisable(GL_SCISSOR_TEST); + renderOffscreenFill(&d); + reloadMatrices = true; + if (hasScissor) + f->glEnable(GL_SCISSOR_TEST); + } + + if (reloadMatrices) { + reloadMatrices = false; + nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, matrix()->constData()); + nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, state->projectionMatrix()->constData()); + } + + // 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); + QMatrix4x4 mv = *matrix(); + mv.translate(d.fallbackTopLeft.x(), d.fallbackTopLeft.y()); + m_fallbackBlitter.texturedQuad(d.fallbackFbo->texture(), d.fallbackFbo->size(), + *state->projectionMatrix(), mv, + 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("Shape/NVPR: stencil clip ref value %d too large; expect rendering errors", sv); + } + + renderStroke(&d, strokeStencilValue, writeMask); + } + + if (stencilClip) + nvpr.pathStencilFunc(GL_ALWAYS, 0, ~0); + + d.dirty = 0; + } + + f->glBindProgramPipeline(0); +} + +QSGRenderNode::StateFlags QQuickShapeNvprRenderNode::changedStates() const +{ + return BlendState | StencilState | DepthState | ScissorState; +} + +QSGRenderNode::RenderingFlags QQuickShapeNvprRenderNode::flags() const +{ + return DepthAwareRendering; // avoid hitting the less optimal no-opaque-batch path in the renderer +} + +bool QQuickShapeNvprRenderNode::isSupported() +{ + static const bool nvprDisabled = qEnvironmentVariableIntValue("QT_NO_NVPR") != 0; + return !nvprDisabled && QQuickNvprFunctions::isSupported(); +} + +bool QQuickNvprBlitter::create() +{ + if (isCreated()) + destroy(); + + m_program = new QOpenGLShaderProgram; + if (QOpenGLContext::currentContext()->format().profile() == QSurfaceFormat::CoreProfile) { + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/shadereffect_core.vert")); + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/shadereffect_core.frag")); + } else { + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/shadereffect.vert")); + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/shadereffect.frag")); + } + m_program->bindAttributeLocation("qt_Vertex", 0); + m_program->bindAttributeLocation("qt_MultiTexCoord0", 1); + if (!m_program->link()) + return false; + + m_matrixLoc = m_program->uniformLocation("qt_Matrix"); + m_opacityLoc = m_program->uniformLocation("qt_Opacity"); + + m_buffer = new QOpenGLBuffer; + if (!m_buffer->create()) + return false; + m_buffer->bind(); + m_buffer->allocate(4 * sizeof(GLfloat) * 6); + m_buffer->release(); + + return true; +} + +void QQuickNvprBlitter::destroy() +{ + if (m_program) { + delete m_program; + m_program = nullptr; + } + if (m_buffer) { + delete m_buffer; + m_buffer = nullptr; + } +} + +void QQuickNvprBlitter::texturedQuad(GLuint textureId, const QSize &size, + const QMatrix4x4 &proj, const QMatrix4x4 &modelview, + float opacity) +{ + QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); + + m_program->bind(); + + QMatrix4x4 m = proj * modelview; + m_program->setUniformValue(m_matrixLoc, m); + m_program->setUniformValue(m_opacityLoc, opacity); + + m_buffer->bind(); + + if (size != m_prevSize) { + m_prevSize = size; + + QPointF p0(size.width() - 1, size.height() - 1); + QPointF p1(0, 0); + QPointF p2(0, size.height() - 1); + QPointF p3(size.width() - 1, 0); + + GLfloat vertices[6 * 4] = { + GLfloat(p0.x()), GLfloat(p0.y()), 1, 0, + GLfloat(p1.x()), GLfloat(p1.y()), 0, 1, + GLfloat(p2.x()), GLfloat(p2.y()), 0, 0, + + GLfloat(p0.x()), GLfloat(p0.y()), 1, 0, + GLfloat(p3.x()), GLfloat(p3.y()), 1, 1, + GLfloat(p1.x()), GLfloat(p1.y()), 0, 1, + }; + + m_buffer->write(0, vertices, sizeof(vertices)); + } + + m_program->enableAttributeArray(0); + m_program->enableAttributeArray(1); + f->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0); + f->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (const void *) (2 * sizeof(GLfloat))); + + f->glBindTexture(GL_TEXTURE_2D, textureId); + + f->glDrawArrays(GL_TRIANGLES, 0, 6); + + f->glBindTexture(GL_TEXTURE_2D, 0); + m_buffer->release(); + m_program->release(); +} + +QT_END_NAMESPACE diff --git a/src/imports/shapes/qquickshapenvprrenderer_p.h b/src/imports/shapes/qquickshapenvprrenderer_p.h new file mode 100644 index 0000000000..33007313d4 --- /dev/null +++ b/src/imports/shapes/qquickshapenvprrenderer_p.h @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSHAPENVPRRENDERER_P_H +#define QQUICKSHAPENVPRRENDERER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickshape_p_p.h" +#include "qquicknvprfunctions_p.h" +#include +#include +#include +#include + +#ifndef QT_NO_OPENGL + +QT_BEGIN_NAMESPACE + +class QQuickShapeNvprRenderNode; +class QOpenGLFramebufferObject; +class QOpenGLBuffer; +class QOpenGLExtraFunctions; + +class QQuickShapeNvprRenderer : public QQuickAbstractPathRenderer +{ +public: + enum Dirty { + DirtyPath = 0x01, + DirtyStyle = 0x02, + DirtyFillRule = 0x04, + DirtyDash = 0x08, + DirtyFillGradient = 0x10, + DirtyList = 0x20 + }; + + void beginSync(int totalCount) override; + void setPath(int index, const QQuickPath *path) override; + void setJSPath(int index, const QQuickShapePathCommands &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, QQuickShapePath::FillRule fillRule) override; + void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) override; + void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) override; + void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) override; + void setFillGradient(int index, QQuickShapeGradient *gradient) override; + void endSync(bool async) override; + + void updateNode() override; + + void setNode(QQuickShapeNvprRenderNode *node); + + struct NvprPath { + QVector cmd; + QVector coord; + QByteArray str; + }; + +private: + struct ShapePathGuiData { + int dirty = 0; + NvprPath path; + qreal strokeWidth; + QColor strokeColor; + QColor fillColor; + QQuickShapePath::JoinStyle joinStyle; + int miterLimit; + QQuickShapePath::CapStyle capStyle; + QQuickShapePath::FillRule fillRule; + bool dashActive; + qreal dashOffset; + QVector dashPattern; + bool fillGradientActive; + QQuickShapeGradientCache::GradientDesc fillGradient; + }; + + void convertPath(const QQuickPath *path, ShapePathGuiData *d); + void convertJSPath(const QQuickShapePathCommands &path, ShapePathGuiData *d); + + QQuickShapeNvprRenderNode *m_node = nullptr; + int m_accDirty = 0; + + QVector m_sp; +}; + +QDebug operator<<(QDebug debug, const QQuickShapeNvprRenderer::NvprPath &path); + +class QQuickNvprMaterialManager +{ +public: + enum Material { + MatSolid, + MatLinearGradient, + + NMaterials + }; + + struct MaterialDesc { + GLuint ppl = 0; + GLuint prg = 0; + int uniLoc[4]; + }; + + void create(QQuickNvprFunctions *nvpr); + MaterialDesc *activateMaterial(Material m); + void releaseResources(); + +private: + QQuickNvprFunctions *m_nvpr; + MaterialDesc m_materials[NMaterials]; +}; + +class QQuickNvprBlitter +{ +public: + bool create(); + void destroy(); + bool isCreated() const { return m_program != nullptr; } + void texturedQuad(GLuint textureId, const QSize &size, + const QMatrix4x4 &proj, const QMatrix4x4 &modelview, + float opacity); + +private: + QOpenGLShaderProgram *m_program = nullptr; + QOpenGLBuffer *m_buffer = nullptr; + int m_matrixLoc; + int m_opacityLoc; + QSize m_prevSize; +}; + +class QQuickShapeNvprRenderNode : public QSGRenderNode +{ +public: + ~QQuickShapeNvprRenderNode(); + + void render(const RenderState *state) override; + void releaseResources() override; + StateFlags changedStates() const override; + RenderingFlags flags() const override; + + static bool isSupported(); + +private: + struct ShapePathRenderData { + GLuint path = 0; + int dirty = 0; + QQuickShapeNvprRenderer::NvprPath source; + GLfloat strokeWidth; + QVector4D strokeColor; + QVector4D fillColor; + GLenum joinStyle; + GLint miterLimit; + GLenum capStyle; + GLenum fillRule; + GLfloat dashOffset; + QVector dashPattern; + bool fillGradientActive; + QQuickShapeGradientCache::GradientDesc fillGradient; + QOpenGLFramebufferObject *fallbackFbo = nullptr; + bool fallbackValid = false; + QSize fallbackSize; + QPointF fallbackTopLeft; + + bool hasFill() const { return !qFuzzyIsNull(fillColor.w()) || fillGradientActive; } + bool hasStroke() const { return strokeWidth >= 0.0f && !qFuzzyIsNull(strokeColor.w()); } + }; + + void updatePath(ShapePathRenderData *d); + void renderStroke(ShapePathRenderData *d, int strokeStencilValue, int writeMask); + void renderFill(ShapePathRenderData *d); + void renderOffscreenFill(ShapePathRenderData *d); + void setupStencilForCover(bool stencilClip, int sv); + + static bool nvprInited; + static QQuickNvprFunctions nvpr; + static QQuickNvprMaterialManager mtlmgr; + + QQuickNvprBlitter m_fallbackBlitter; + QOpenGLExtraFunctions *f = nullptr; + + QVector m_sp; + + friend class QQuickShapeNvprRenderer; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_OPENGL + +#endif // QQUICKSHAPENVPRRENDERER_P_H diff --git a/src/imports/shapes/qquickshapesoftwarerenderer.cpp b/src/imports/shapes/qquickshapesoftwarerenderer.cpp new file mode 100644 index 0000000000..33d80be22c --- /dev/null +++ b/src/imports/shapes/qquickshapesoftwarerenderer.cpp @@ -0,0 +1,277 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickshapesoftwarerenderer_p.h" +#include + +QT_BEGIN_NAMESPACE + +void QQuickShapeSoftwareRenderer::beginSync(int totalCount) +{ + if (m_sp.count() != totalCount) { + m_sp.resize(totalCount); + m_accDirty |= DirtyList; + } +} + +void QQuickShapeSoftwareRenderer::setPath(int index, const QQuickPath *path) +{ + ShapePathGuiData &d(m_sp[index]); + d.path = path ? path->path() : QPainterPath(); + d.dirty |= DirtyPath; + m_accDirty |= DirtyPath; +} + +void QQuickShapeSoftwareRenderer::setJSPath(int index, const QQuickShapePathCommands &path) +{ + ShapePathGuiData &d(m_sp[index]); + d.path = path.toPainterPath(); + d.dirty |= DirtyPath; + m_accDirty |= DirtyPath; +} + +void QQuickShapeSoftwareRenderer::setStrokeColor(int index, const QColor &color) +{ + ShapePathGuiData &d(m_sp[index]); + d.pen.setColor(color); + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; +} + +void QQuickShapeSoftwareRenderer::setStrokeWidth(int index, qreal w) +{ + ShapePathGuiData &d(m_sp[index]); + d.strokeWidth = w; + if (w >= 0.0f) + d.pen.setWidthF(w); + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; +} + +void QQuickShapeSoftwareRenderer::setFillColor(int index, const QColor &color) +{ + ShapePathGuiData &d(m_sp[index]); + d.fillColor = color; + d.brush.setColor(color); + d.dirty |= DirtyBrush; + m_accDirty |= DirtyBrush; +} + +void QQuickShapeSoftwareRenderer::setFillRule(int index, QQuickShapePath::FillRule fillRule) +{ + ShapePathGuiData &d(m_sp[index]); + d.fillRule = Qt::FillRule(fillRule); + d.dirty |= DirtyFillRule; + m_accDirty |= DirtyFillRule; +} + +void QQuickShapeSoftwareRenderer::setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) +{ + ShapePathGuiData &d(m_sp[index]); + d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); + d.pen.setMiterLimit(miterLimit); + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; +} + +void QQuickShapeSoftwareRenderer::setCapStyle(int index, QQuickShapePath::CapStyle capStyle) +{ + ShapePathGuiData &d(m_sp[index]); + d.pen.setCapStyle(Qt::PenCapStyle(capStyle)); + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; +} + +void QQuickShapeSoftwareRenderer::setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) +{ + ShapePathGuiData &d(m_sp[index]); + switch (strokeStyle) { + case QQuickShapePath::SolidLine: + d.pen.setStyle(Qt::SolidLine); + break; + case QQuickShapePath::DashLine: + d.pen.setStyle(Qt::CustomDashLine); + d.pen.setDashPattern(dashPattern); + d.pen.setDashOffset(dashOffset); + break; + default: + break; + } + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; +} + +void QQuickShapeSoftwareRenderer::setFillGradient(int index, QQuickShapeGradient *gradient) +{ + ShapePathGuiData &d(m_sp[index]); + if (QQuickShapeLinearGradient *linearGradient = qobject_cast(gradient)) { + QLinearGradient painterGradient(linearGradient->x1(), linearGradient->y1(), + linearGradient->x2(), linearGradient->y2()); + painterGradient.setStops(linearGradient->sortedGradientStops()); + switch (gradient->spread()) { + case QQuickShapeGradient::PadSpread: + painterGradient.setSpread(QGradient::PadSpread); + break; + case QQuickShapeGradient::RepeatSpread: + painterGradient.setSpread(QGradient::RepeatSpread); + break; + case QQuickShapeGradient::ReflectSpread: + painterGradient.setSpread(QGradient::ReflectSpread); + break; + default: + break; + } + d.brush = QBrush(painterGradient); + } else { + d.brush = QBrush(d.fillColor); + } + d.dirty |= DirtyBrush; + m_accDirty |= DirtyBrush; +} + +void QQuickShapeSoftwareRenderer::endSync(bool) +{ +} + +void QQuickShapeSoftwareRenderer::setNode(QQuickShapeSoftwareRenderNode *node) +{ + if (m_node != node) { + m_node = node; + m_accDirty |= DirtyList; + } +} + +void QQuickShapeSoftwareRenderer::updateNode() +{ + if (!m_accDirty) + return; + + const int count = m_sp.count(); + const bool listChanged = m_accDirty & DirtyList; + if (listChanged) + m_node->m_sp.resize(count); + + m_node->m_boundingRect = QRectF(); + + for (int i = 0; i < count; ++i) { + ShapePathGuiData &src(m_sp[i]); + QQuickShapeSoftwareRenderNode::ShapePathRenderData &dst(m_node->m_sp[i]); + + if (listChanged || (src.dirty & DirtyPath)) { + dst.path = src.path; + dst.path.setFillRule(src.fillRule); + } + + 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; + + src.dirty = 0; + + QRectF br = dst.path.boundingRect(); + const float sw = qMax(1.0f, dst.strokeWidth); + br.adjust(-sw, -sw, sw, sw); + m_node->m_boundingRect |= br; + } + + m_node->markDirty(QSGNode::DirtyMaterial); + m_accDirty = 0; +} + +QQuickShapeSoftwareRenderNode::QQuickShapeSoftwareRenderNode(QQuickShape *item) + : m_item(item) +{ +} + +QQuickShapeSoftwareRenderNode::~QQuickShapeSoftwareRenderNode() +{ + releaseResources(); +} + +void QQuickShapeSoftwareRenderNode::releaseResources() +{ +} + +void QQuickShapeSoftwareRenderNode::render(const RenderState *state) +{ + if (m_sp.isEmpty()) + return; + + QSGRendererInterface *rif = m_item->window()->rendererInterface(); + QPainter *p = static_cast(rif->getResource(m_item->window(), QSGRendererInterface::PainterResource)); + Q_ASSERT(p); + + const QRegion *clipRegion = state->clipRegion(); + if (clipRegion && !clipRegion->isEmpty()) + p->setClipRegion(*clipRegion, Qt::ReplaceClip); // must be done before setTransform + + p->setTransform(matrix()->toTransform()); + p->setOpacity(inheritedOpacity()); + + for (const ShapePathRenderData &d : qAsConst(m_sp)) { + 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 QQuickShapeSoftwareRenderNode::changedStates() const +{ + return 0; +} + +QSGRenderNode::RenderingFlags QQuickShapeSoftwareRenderNode::flags() const +{ + return BoundedRectRendering; // avoid fullscreen updates by saying we won't draw outside rect() +} + +QRectF QQuickShapeSoftwareRenderNode::rect() const +{ + return m_boundingRect; +} + +QT_END_NAMESPACE diff --git a/src/imports/shapes/qquickshapesoftwarerenderer_p.h b/src/imports/shapes/qquickshapesoftwarerenderer_p.h new file mode 100644 index 0000000000..53982ce347 --- /dev/null +++ b/src/imports/shapes/qquickshapesoftwarerenderer_p.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSHAPESOFTWARERENDERER_P_H +#define QQUICKSHAPESOFTWARERENDERER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickshape_p_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickShapeSoftwareRenderNode; + +class QQuickShapeSoftwareRenderer : public QQuickAbstractPathRenderer +{ +public: + enum Dirty { + DirtyPath = 0x01, + DirtyPen = 0x02, + DirtyFillRule = 0x04, + DirtyBrush = 0x08, + DirtyList = 0x10 + }; + + void beginSync(int totalCount) override; + void setPath(int index, const QQuickPath *path) override; + void setJSPath(int index, const QQuickShapePathCommands &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, QQuickShapePath::FillRule fillRule) override; + void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) override; + void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) override; + void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) override; + void setFillGradient(int index, QQuickShapeGradient *gradient) override; + void endSync(bool async) override; + + void updateNode() override; + + void setNode(QQuickShapeSoftwareRenderNode *node); + +private: + QQuickShapeSoftwareRenderNode *m_node = nullptr; + int m_accDirty = 0; + struct ShapePathGuiData { + int dirty = 0; + QPainterPath path; + QPen pen; + float strokeWidth; + QColor fillColor; + QBrush brush; + Qt::FillRule fillRule; + }; + QVector m_sp; +}; + +class QQuickShapeSoftwareRenderNode : public QSGRenderNode +{ +public: + QQuickShapeSoftwareRenderNode(QQuickShape *item); + ~QQuickShapeSoftwareRenderNode(); + + void render(const RenderState *state) override; + void releaseResources() override; + StateFlags changedStates() const override; + RenderingFlags flags() const override; + QRectF rect() const override; + +private: + QQuickShape *m_item; + + struct ShapePathRenderData { + QPainterPath path; + QPen pen; + float strokeWidth; + QBrush brush; + }; + QVector m_sp; + QRectF m_boundingRect; + + friend class QQuickShapeSoftwareRenderer; +}; + +QT_END_NAMESPACE + +#endif // QQUICKSHAPESOFTWARERENDERER_P_H diff --git a/src/imports/shapes/shapes.pro b/src/imports/shapes/shapes.pro new file mode 100644 index 0000000000..80e6a22142 --- /dev/null +++ b/src/imports/shapes/shapes.pro @@ -0,0 +1,31 @@ +CXX_MODULE = qml +TARGET = qmlshapesplugin +TARGETPATH = QtQuick/Shapes +IMPORT_VERSION = 1.0 + +QT = core gui qml quick quick-private + +HEADERS += \ + qquickshape_p.h \ + qquickshape_p_p.h \ + qquickshapegenericrenderer_p.h \ + qquickshapesoftwarerenderer_p.h + +SOURCES += \ + plugin.cpp \ + qquickshape.cpp \ + qquickshapegenericrenderer.cpp \ + qquickshapesoftwarerenderer.cpp + +qtConfig(opengl) { + HEADERS += \ + qquicknvprfunctions_p.h \ + qquicknvprfunctions_p_p.h \ + qquickshapenvprrenderer_p.h + + SOURCES += \ + qquicknvprfunctions.cpp \ + qquickshapenvprrenderer.cpp +} + +load(qml_plugin) diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp index 84441e308f..085963be6c 100644 --- a/src/quick/util/qquickpath.cpp +++ b/src/quick/util/qquickpath.cpp @@ -68,7 +68,7 @@ QT_BEGIN_NAMESPACE \instantiates QQuickPath \inqmlmodule QtQuick \ingroup qtquick-animation-paths - \brief Defines a path for use by \l PathView and \l PathItem + \brief Defines a path for use by \l PathView and \l Shape A Path is composed of one or more path segments - PathLine, PathQuad, PathCubic, PathArc, PathCurve, PathSvg. @@ -80,16 +80,16 @@ QT_BEGIN_NAMESPACE 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 + \l PathView and \l Shape. 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 + \li Shape + \li Shape, GL_NV_path_rendering + \li Shape, software \row \li PathMove \li N/A @@ -146,7 +146,7 @@ QT_BEGIN_NAMESPACE \li No \endtable - \sa PathView, PathItem, PathAttribute, PathPercent, PathLine, PathMove, PathQuad, PathCubic, PathArc, PathCurve, PathSvg + \sa PathView, Shape, PathAttribute, PathPercent, PathLine, PathMove, PathQuad, PathCubic, PathArc, PathCurve, PathSvg */ QQuickPath::QQuickPath(QObject *parent) : QObject(*(new QQuickPathPrivate), parent) @@ -1119,7 +1119,7 @@ void QQuickPathLine::addToPath(QPainterPath &path, const QQuickPathData &data) \ingroup qtquick-animation-paths \brief Moves the Path's position - While not relevant with PathView, for Path elements used with PathItem it + While not relevant with PathView, for Path elements used with Shape it is important to distinguish between the operations of drawing a straight line and moving the path position without drawing anything. @@ -1927,7 +1927,7 @@ void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data) \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 + \l Shape 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 -- cgit v1.2.3 From af6655885cf19de69d5f57ac9daee9f6b9935a70 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 1 Jun 2017 10:48:00 +0200 Subject: QQuickWindow/View: set a QObject-parent on the root item People are often confused why eg. the objects from: window->contentItem()->findChildren() are not included in window->findChildren(). This change connects the item tree to the window's object tree to make findChild() and findChildren() produce expected results. The same technique is already used for QQuickFlickable's contentItem. [ChangeLog][QtQuick][QQuickWindow] Set the window as the QObject-parent of the contentItem to ensure consistent behavior for calling findChildren() on QQuickWindow and QQuickWindow::contentItem. [ChangeLog][QtQuick][QQuickView] Set the window's contentItem as the QObject-parent of the rootObject to ensure consistent behavior for calling findChildren() on QQuickWindow::contentItem and QQuickView::rootObject. Change-Id: Idb7834eb5e560088ca849e6ce90e6fa3b3ae3e91 Reviewed-by: Mitch Curtis --- src/quick/items/qquickview.cpp | 1 + src/quick/items/qquickwindow.cpp | 1 + 2 files changed, 2 insertions(+) (limited to 'src') diff --git a/src/quick/items/qquickview.cpp b/src/quick/items/qquickview.cpp index 8313b53a7d..fca1805fc9 100644 --- a/src/quick/items/qquickview.cpp +++ b/src/quick/items/qquickview.cpp @@ -496,6 +496,7 @@ void QQuickViewPrivate::setRootObject(QObject *obj) if (QQuickItem *sgItem = qobject_cast(obj)) { root = sgItem; sgItem->setParentItem(q->QQuickWindow::contentItem()); + QQml_setParent_noEvent(sgItem, q->QQuickWindow::contentItem()); } else if (qobject_cast(obj)) { qWarning() << "QQuickView does not support using windows as a root item." << endl << endl diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index c441cfc357..d30f1c3f78 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -526,6 +526,7 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control) Q_Q(QQuickWindow); contentItem = new QQuickRootItem; + QQml_setParent_noEvent(contentItem, c); QQmlEngine::setObjectOwnership(contentItem, QQmlEngine::CppOwnership); QQuickItemPrivate *contentItemPrivate = QQuickItemPrivate::get(contentItem); contentItemPrivate->window = q; -- cgit v1.2.3 From d77587b95f490ba9be372acfb81a1659f2844b2f Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 18 Apr 2017 16:51:28 -0700 Subject: Move Q_REQUIRED_RESULT to the beginning of the declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit That's the correct place and works with C++11 attributes. Change-Id: I7814054a102a407d876ffffd14b6a2f74423c222 Reviewed-by: Sérgio Martins --- src/qml/compiler/qqmlpropertyvalidator_p.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/qml/compiler/qqmlpropertyvalidator_p.h b/src/qml/compiler/qqmlpropertyvalidator_p.h index d0bd314461..e37b8141f4 100644 --- a/src/qml/compiler/qqmlpropertyvalidator_p.h +++ b/src/qml/compiler/qqmlpropertyvalidator_p.h @@ -69,8 +69,8 @@ private: bool canCoerce(int to, QQmlPropertyCache *fromMo) const; - QVector recordError(const QV4::CompiledData::Location &location, const QString &description) const Q_REQUIRED_RESULT; - QVector recordError(const QQmlCompileError &error) const Q_REQUIRED_RESULT; + Q_REQUIRED_RESULT QVector recordError(const QV4::CompiledData::Location &location, const QString &description) const; + Q_REQUIRED_RESULT QVector recordError(const QQmlCompileError &error) const; QString stringAt(int index) const { return qmlUnit->stringAt(index); } QQmlEnginePrivate *enginePrivate; -- cgit v1.2.3 From d7b45f7dc1d5f28ecdb022c5a4c7673bc55257c8 Mon Sep 17 00:00:00 2001 From: Sami Nurmenniemi Date: Mon, 29 May 2017 10:21:38 +0300 Subject: Add Qt.platform.pluginName property This exposes QGuiApplication::platformName() for qml. It is required at least for tests that are run on the "offscreen" platform (armv7 tests on qemu). [ChangeLog][QtQuick] Added Qt.platform.pluginName property. Task-number: QTBUG-60268 Change-Id: Ie55a282485d4d76ffe7ed8e77359ad7183f579c2 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlengine.cpp | 6 ++++++ src/qml/qml/qqmlglobal.cpp | 2 ++ src/qml/qml/qqmlglobal_p.h | 1 + src/qml/qml/qqmlplatform.cpp | 6 ++++++ src/qml/qml/qqmlplatform_p.h | 2 ++ src/quick/util/qquickglobal.cpp | 5 +++++ 6 files changed, 22 insertions(+) (limited to 'src') diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 4a21acb050..d8779e0d12 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -449,6 +449,12 @@ The following functions are also on the Qt object. \li \c "windows" - Windows \li \c "winrt" - WinRT / UWP \endlist + + \row + \li \c platform.pluginName + \li This is the name of the platform set on the QGuiApplication instance + as returned by \l QGuiApplication::platformName() + \endtable */ diff --git a/src/qml/qml/qqmlglobal.cpp b/src/qml/qml/qqmlglobal.cpp index 7939656107..6418812bae 100644 --- a/src/qml/qml/qqmlglobal.cpp +++ b/src/qml/qml/qqmlglobal.cpp @@ -343,6 +343,8 @@ QObject *QQmlGuiProvider::styleHints() return o; } +QString QQmlGuiProvider::pluginName() const { return QString(); } + static QQmlGuiProvider *guiProvider = 0; Q_QML_PRIVATE_EXPORT QQmlGuiProvider *QQml_setGuiProvider(QQmlGuiProvider *newProvider) diff --git a/src/qml/qml/qqmlglobal_p.h b/src/qml/qml/qqmlglobal_p.h index 707814e781..a6c113f5a7 100644 --- a/src/qml/qml/qqmlglobal_p.h +++ b/src/qml/qml/qqmlglobal_p.h @@ -311,6 +311,7 @@ public: virtual QObject *styleHints(); virtual QStringList fontFamilies(); virtual bool openUrlExternally(QUrl &); + virtual QString pluginName() const; }; Q_QML_PRIVATE_EXPORT QQmlGuiProvider *QQml_setGuiProvider(QQmlGuiProvider *); diff --git a/src/qml/qml/qqmlplatform.cpp b/src/qml/qml/qqmlplatform.cpp index 165cde5eb3..a8c402af2e 100644 --- a/src/qml/qml/qqmlplatform.cpp +++ b/src/qml/qml/qqmlplatform.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "qqmlplatform_p.h" +#include "qqmlglobal_p.h" QT_BEGIN_NAMESPACE @@ -78,6 +79,11 @@ QString QQmlPlatform::os() #endif } +QString QQmlPlatform::pluginName() const +{ + return QQml_guiProvider()->pluginName(); +} + QT_END_NAMESPACE #include "moc_qqmlplatform_p.cpp" diff --git a/src/qml/qml/qqmlplatform_p.h b/src/qml/qml/qqmlplatform_p.h index 6887720adb..6246ca7105 100644 --- a/src/qml/qml/qqmlplatform_p.h +++ b/src/qml/qml/qqmlplatform_p.h @@ -61,12 +61,14 @@ class Q_QML_PRIVATE_EXPORT QQmlPlatform : public QObject { Q_OBJECT Q_PROPERTY(QString os READ os CONSTANT) + Q_PROPERTY(QString pluginName READ pluginName CONSTANT) public: explicit QQmlPlatform(QObject *parent = 0); virtual ~QQmlPlatform(); static QString os(); + QString pluginName() const; private: Q_DISABLE_COPY(QQmlPlatform) diff --git a/src/quick/util/qquickglobal.cpp b/src/quick/util/qquickglobal.cpp index 2070fd7ff0..1d2f3de1df 100644 --- a/src/quick/util/qquickglobal.cpp +++ b/src/quick/util/qquickglobal.cpp @@ -814,6 +814,11 @@ public: return false; #endif } + + QString pluginName() const override + { + return QGuiApplication::platformName(); + } }; -- cgit v1.2.3 From 5af4c9b237a23ce12ca7c56eb6c9ecda17743228 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 2 Jun 2017 15:12:05 +0200 Subject: Reduce objects: Make ShapePath inherit Path Shape { ShapePath { Path { ... } } } simply becomes Shape { ShapePath { ... } } Change-Id: Ie57936cd7953c8a8d6c67e78b9d73bdbe2a05316 Reviewed-by: Mitch Curtis --- src/imports/shapes/qquickshape.cpp | 122 +++++++++++++---------------------- src/imports/shapes/qquickshape_p.h | 14 +--- src/imports/shapes/qquickshape_p_p.h | 3 +- src/quick/util/qquickpath.cpp | 5 ++ src/quick/util/qquickpath_p.h | 1 + src/quick/util/qquickpath_p_p.h | 2 +- 6 files changed, 55 insertions(+), 92 deletions(-) (limited to 'src') diff --git a/src/imports/shapes/qquickshape.cpp b/src/imports/shapes/qquickshape.cpp index e37addba07..81736fcd82 100644 --- a/src/imports/shapes/qquickshape.cpp +++ b/src/imports/shapes/qquickshape.cpp @@ -133,17 +133,17 @@ QPainterPath QQuickShapePathCommands::toPainterPath() const \inqmlmodule QtQuick.Shapes \ingroup qtquick-paths \ingroup qtquick-views - \inherits Object + \inherits Path \brief Describes a Path and associated properties for stroking and filling \since 5.10 - A Shape contains one or more ShapePath elements. At least one - ShapePath is necessary in order to have a Shape output anything - visible. A ShapeItem in turn contains a Path and properties describing the - stroking and filling parameters, such as the stroke width and color, the - fill color or gradient, join and cap styles, and so on. Finally, the Path - object contains a list of path elements like PathMove, PathLine, PathCubic, - PathQuad, PathArc. + A Shape contains one or more ShapePath elements. At least one ShapePath is + necessary in order to have a Shape output anything visible. A ShapePath + itself is a \l Path with additional properties describing the stroking and + filling parameters, such as the stroke width and color, the fill color or + gradient, join and cap styles, and so on. As with ordinary \l Path objects, + ShapePath also contains a list of path elements like \l PathMove, \l PathLine, + \l PathCubic, \l PathQuad, \l PathArc, together with a starting position. Any property changes in these data sets will be bubble up and change the output of the Shape. This means that it is simple and easy to change, or @@ -166,12 +166,10 @@ QPainterPath QQuickShapePathCommands::toPainterPath() const joinStyle: styles[joinStyleIndex] - Path { - startX: 30 - startY: 30 - PathLine { x: 100; y: 100 } - PathLine { x: 30; y: 100 } - } + startX: 30 + startY: 30 + PathLine { x: 100; y: 100 } + PathLine { x: 30; y: 100 } } \endcode @@ -182,57 +180,27 @@ QPainterPath QQuickShapePathCommands::toPainterPath() const */ QQuickShapePathPrivate::QQuickShapePathPrivate() - : path(nullptr), - dirty(DirtyAll) + : dirty(DirtyAll) { } QQuickShapePath::QQuickShapePath(QObject *parent) - : QObject(*(new QQuickShapePathPrivate), parent) + : QQuickPath(*(new QQuickShapePathPrivate), parent) { -} + // The inherited changed() and the shapePathChanged() signals remain + // distinct, and this is intentional. Combining the two is not possible due + // to the difference in semantics and the need to act (see dirty flag + // below) differently on QQuickPath-related changes. -QQuickShapePath::~QQuickShapePath() -{ + connect(this, &QQuickPath::changed, [this]() { + Q_D(QQuickShapePath); + d->dirty |= QQuickShapePathPrivate::DirtyPath; + emit shapePathChanged(); + }); } -/*! - \qmlproperty Path QtQuick.Shapes::ShapePath::path - - This property holds the Path object. - - \default - */ - -QQuickPath *QQuickShapePath::path() const -{ - Q_D(const QQuickShapePath); - return d->path; -} - -void QQuickShapePath::setPath(QQuickPath *path) -{ - Q_D(QQuickShapePath); - if (d->path == path) - return; - - if (d->path) - qmlobject_disconnect(d->path, QQuickPath, SIGNAL(changed()), - this, QQuickShapePath, SLOT(_q_pathChanged())); - d->path = path; - qmlobject_connect(d->path, QQuickPath, SIGNAL(changed()), - this, QQuickShapePath, SLOT(_q_pathChanged())); - - d->dirty |= QQuickShapePathPrivate::DirtyPath; - emit pathChanged(); - emit changed(); -} - -void QQuickShapePathPrivate::_q_pathChanged() +QQuickShapePath::~QQuickShapePath() { - Q_Q(QQuickShapePath); - dirty |= DirtyPath; - emit q->changed(); } /*! @@ -258,7 +226,7 @@ void QQuickShapePath::setStrokeColor(const QColor &color) d->sfp.strokeColor = color; d->dirty |= QQuickShapePathPrivate::DirtyStrokeColor; emit strokeColorChanged(); - emit changed(); + emit shapePathChanged(); } } @@ -285,7 +253,7 @@ void QQuickShapePath::setStrokeWidth(qreal w) d->sfp.strokeWidth = w; d->dirty |= QQuickShapePathPrivate::DirtyStrokeWidth; emit strokeWidthChanged(); - emit changed(); + emit shapePathChanged(); } } @@ -312,7 +280,7 @@ void QQuickShapePath::setFillColor(const QColor &color) d->sfp.fillColor = color; d->dirty |= QQuickShapePathPrivate::DirtyFillColor; emit fillColorChanged(); - emit changed(); + emit shapePathChanged(); } } @@ -342,7 +310,7 @@ void QQuickShapePath::setFillRule(FillRule fillRule) d->sfp.fillRule = fillRule; d->dirty |= QQuickShapePathPrivate::DirtyFillRule; emit fillRuleChanged(); - emit changed(); + emit shapePathChanged(); } } @@ -372,7 +340,7 @@ void QQuickShapePath::setJoinStyle(JoinStyle style) d->sfp.joinStyle = style; d->dirty |= QQuickShapePathPrivate::DirtyStyle; emit joinStyleChanged(); - emit changed(); + emit shapePathChanged(); } } @@ -398,7 +366,7 @@ void QQuickShapePath::setMiterLimit(int limit) d->sfp.miterLimit = limit; d->dirty |= QQuickShapePathPrivate::DirtyStyle; emit miterLimitChanged(); - emit changed(); + emit shapePathChanged(); } } @@ -428,7 +396,7 @@ void QQuickShapePath::setCapStyle(CapStyle style) d->sfp.capStyle = style; d->dirty |= QQuickShapePathPrivate::DirtyStyle; emit capStyleChanged(); - emit changed(); + emit shapePathChanged(); } } @@ -457,7 +425,7 @@ void QQuickShapePath::setStrokeStyle(StrokeStyle style) d->sfp.strokeStyle = style; d->dirty |= QQuickShapePathPrivate::DirtyDash; emit strokeStyleChanged(); - emit changed(); + emit shapePathChanged(); } } @@ -485,7 +453,7 @@ void QQuickShapePath::setDashOffset(qreal offset) d->sfp.dashOffset = offset; d->dirty |= QQuickShapePathPrivate::DirtyDash; emit dashOffsetChanged(); - emit changed(); + emit shapePathChanged(); } } @@ -516,7 +484,7 @@ void QQuickShapePath::setDashPattern(const QVector &array) d->sfp.dashPattern = array; d->dirty |= QQuickShapePathPrivate::DirtyDash; emit dashPatternChanged(); - emit changed(); + emit shapePathChanged(); } } @@ -549,7 +517,7 @@ void QQuickShapePath::setFillGradient(QQuickShapeGradient *gradient) qmlobject_connect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()), this, QQuickShapePath, SLOT(_q_fillGradientChanged())); d->dirty |= QQuickShapePathPrivate::DirtyFillGradient; - emit changed(); + emit shapePathChanged(); } } @@ -557,7 +525,7 @@ void QQuickShapePathPrivate::_q_fillGradientChanged() { Q_Q(QQuickShapePath); dirty |= DirtyFillGradient; - emit q->changed(); + emit q->shapePathChanged(); } void QQuickShapePath::resetFillGradient() @@ -613,12 +581,10 @@ void QQuickShapePath::resetFillGradient() } strokeStyle: ShapePath.DashLine dashPattern: [ 1, 4 ] - Path { - startX: 20; startY: 20 - PathLine { x: 180; y: 130 } - PathLine { x: 20; y: 130 } - PathLine { x: 20; y: 20 } - } + startX: 20; startY: 20 + PathLine { x: 180; y: 130 } + PathLine { x: 20; y: 130 } + PathLine { x: 20; y: 20 } } } \endcode @@ -838,7 +804,7 @@ static void vpe_append(QQmlListProperty *property, QQuickShapeP d->qmlData.sp.append(obj); if (d->componentComplete) { - QObject::connect(obj, SIGNAL(changed()), item, SLOT(_q_shapePathChanged())); + QObject::connect(obj, SIGNAL(shapePathChanged()), item, SLOT(_q_shapePathChanged())); d->_q_shapePathChanged(); } } @@ -855,7 +821,7 @@ static void vpe_clear(QQmlListProperty *property) QQuickShapePrivate *d = QQuickShapePrivate::get(item); for (QQuickShapePath *p : d->qmlData.sp) - QObject::disconnect(p, SIGNAL(changed()), item, SLOT(_q_shapePathChanged())); + QObject::disconnect(p, SIGNAL(shapePathChanged()), item, SLOT(_q_shapePathChanged())); d->qmlData.sp.clear(); @@ -894,7 +860,7 @@ void QQuickShape::componentComplete() d->componentComplete = true; for (QQuickShapePath *p : d->qmlData.sp) - connect(p, SIGNAL(changed()), this, SLOT(_q_shapePathChanged())); + connect(p, SIGNAL(shapePathChanged()), this, SLOT(_q_shapePathChanged())); d->_q_shapePathChanged(); } @@ -1042,7 +1008,7 @@ void QQuickShapePrivate::sync() int &dirty(QQuickShapePathPrivate::get(p)->dirty); if (dirty & QQuickShapePathPrivate::DirtyPath) - renderer->setPath(i, p->path()); + renderer->setPath(i, p); if (dirty & QQuickShapePathPrivate::DirtyStrokeColor) renderer->setStrokeColor(i, p->strokeColor()); if (dirty & QQuickShapePathPrivate::DirtyStrokeWidth) diff --git a/src/imports/shapes/qquickshape_p.h b/src/imports/shapes/qquickshape_p.h index 5b3580dd1b..a01c36e971 100644 --- a/src/imports/shapes/qquickshape_p.h +++ b/src/imports/shapes/qquickshape_p.h @@ -54,7 +54,7 @@ #include "qquickitem.h" #include -#include +#include #include #include @@ -152,13 +152,10 @@ private: QPointF m_end; }; -class QQuickShapePath : public QObject +class QQuickShapePath : public QQuickPath { Q_OBJECT - 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) Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged) @@ -201,9 +198,6 @@ public: QQuickShapePath(QObject *parent = nullptr); ~QQuickShapePath(); - QQuickPath *path() const; - void setPath(QQuickPath *path); - QColor strokeColor() const; void setStrokeColor(const QColor &color); @@ -239,8 +233,7 @@ public: void resetFillGradient(); Q_SIGNALS: - void changed(); - void pathChanged(); + void shapePathChanged(); void strokeColorChanged(); void strokeWidthChanged(); void fillColorChanged(); @@ -256,7 +249,6 @@ Q_SIGNALS: private: Q_DISABLE_COPY(QQuickShapePath) Q_DECLARE_PRIVATE(QQuickShapePath) - Q_PRIVATE_SLOT(d_func(), void _q_pathChanged()) Q_PRIVATE_SLOT(d_func(), void _q_fillGradientChanged()) }; diff --git a/src/imports/shapes/qquickshape_p_p.h b/src/imports/shapes/qquickshape_p_p.h index 7a503e36a9..a8a5675ccb 100644 --- a/src/imports/shapes/qquickshape_p_p.h +++ b/src/imports/shapes/qquickshape_p_p.h @@ -131,7 +131,7 @@ public: Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickAbstractPathRenderer::Flags) -class QQuickShapePathPrivate : public QObjectPrivate +class QQuickShapePathPrivate : public QQuickPathPrivate { Q_DECLARE_PUBLIC(QQuickShapePath) @@ -156,7 +156,6 @@ public: static QQuickShapePathPrivate *get(QQuickShapePath *p) { return p->d_func(); } - QQuickPath *path; int dirty; QQuickShapeStrokeFillParams sfp; }; diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp index 085963be6c..0103f66814 100644 --- a/src/quick/util/qquickpath.cpp +++ b/src/quick/util/qquickpath.cpp @@ -153,6 +153,11 @@ QQuickPath::QQuickPath(QObject *parent) { } +QQuickPath::QQuickPath(QQuickPathPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ +} + QQuickPath::~QQuickPath() { } diff --git a/src/quick/util/qquickpath_p.h b/src/quick/util/qquickpath_p.h index 1c639db9ce..b7fde5c272 100644 --- a/src/quick/util/qquickpath_p.h +++ b/src/quick/util/qquickpath_p.h @@ -419,6 +419,7 @@ Q_SIGNALS: void startYChanged(); protected: + QQuickPath(QQuickPathPrivate &dd, QObject *parent = nullptr); void componentComplete() override; void classBegin() override; void disconnectPathElements(); diff --git a/src/quick/util/qquickpath_p_p.h b/src/quick/util/qquickpath_p_p.h index 1dc3c1c47a..8ce85dbf0f 100644 --- a/src/quick/util/qquickpath_p_p.h +++ b/src/quick/util/qquickpath_p_p.h @@ -64,7 +64,7 @@ QT_REQUIRE_CONFIG(quick_path); QT_BEGIN_NAMESPACE -class QQuickPathPrivate : public QObjectPrivate +class Q_QUICK_PRIVATE_EXPORT QQuickPathPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QQuickPath) -- cgit v1.2.3 From a6eac7e299016c9ee8742abffd780683adac3066 Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Mon, 22 May 2017 11:00:52 +0200 Subject: Repeater: Clarify documentation a little Change-Id: I4544dafd4d641728394de9a97e7ae9dd029e91a4 Task-number: QTBUG-57155 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickrepeater.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickrepeater.cpp b/src/quick/items/qquickrepeater.cpp index 9ad7d27b18..6fc4c0553a 100644 --- a/src/quick/items/qquickrepeater.cpp +++ b/src/quick/items/qquickrepeater.cpp @@ -314,7 +314,11 @@ void QQuickRepeater::setDelegate(QQmlComponent *delegate) /*! \qmlproperty int QtQuick::Repeater::count - This property holds the number of items in the repeater. + This property holds the number of items in the model. + + \note The number of items in the model as reported by count may differ from + the number of created delegates if the Repeater is in the process of + instantiating delegates or is incorrectly set up. */ int QQuickRepeater::count() const { -- cgit v1.2.3 From d2c09b41ddc82dda5c5228b1f295850bb1c58f4c Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 7 Jun 2017 11:35:20 +0200 Subject: Jit: fix unary minus for integers Change-Id: Ib2cdfe6f09528d169e9ea6f8b872de875317c9c9 Reviewed-by: Simon Hausmann --- src/qml/jit/qv4unop.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src') diff --git a/src/qml/jit/qv4unop.cpp b/src/qml/jit/qv4unop.cpp index 896be07ed5..78546e1509 100644 --- a/src/qml/jit/qv4unop.cpp +++ b/src/qml/jit/qv4unop.cpp @@ -83,6 +83,15 @@ template void Unop::generateUMinus(IR::Expr *source, IR::Expr *target) { IR::Temp *targetTemp = target->asTemp(); + + if (IR::Const *c = source->asConst()) { + if (c->value == 0 && source->type == IR::SInt32Type) { + // special case: minus integer 0 is 0, which is not what JS expects it to be, so always + // do a runtime call + generateRuntimeCall(_as, target, uMinus, PointerToValue(source)); + return; + } + } if (source->type == IR::SInt32Type) { typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister; if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) -- cgit v1.2.3 From e1bd2860cb7ac7e0c7e52a39aeae23882c260112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Martins?= Date: Mon, 5 Jun 2017 14:20:28 +0100 Subject: Don't detach temporary container Change-Id: I3edebdd149d901992b7db5410bb871ba1bcd454e Reviewed-by: Laszlo Agocs --- .../scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp index ce08d18057..e5b9430236 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp @@ -232,8 +232,8 @@ void QSGSoftwareRenderableNode::update() m_boundingRectMin = QRect(); m_boundingRectMax = QRect(); } else { - m_boundingRectMin = m_boundingRectMin.intersected(m_clipRegion.rects().first()); - m_boundingRectMax = m_boundingRectMax.intersected(m_clipRegion.rects().first()); + m_boundingRectMin = m_boundingRectMin.intersected(m_clipRegion.rects().constFirst()); + m_boundingRectMax = m_boundingRectMax.intersected(m_clipRegion.rects().constFirst()); } } -- cgit v1.2.3 From 0a34deef4d824884e73a93400b854399f7fde4ba Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 7 Jun 2017 12:57:01 +0200 Subject: Fix integer division 0/-1 == -0.0 in JS, which cannot be encoded in an int. Change-Id: Ice5b09fa3d42dc24d543d4581d77c6bfa743b2ca Reviewed-by: Simon Hausmann Reviewed-by: Lars Knoll --- src/qml/jsruntime/qv4runtime.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 37a2bfdf90..ecf2fd8b11 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -1706,7 +1706,9 @@ ReturnedValue Runtime::method_div(const Value &left, const Value &right) if (Value::integerCompatible(left, right)) { int lval = left.integerValue(); int rval = right.integerValue(); - if (rval != 0 && (lval % rval == 0)) + if (rval != 0 // division by zero should result in a NaN + && (lval % rval == 0) // fractions can't be stored in an int + && !(lval == 0 && rval < 0)) // 0 / -something results in -0.0 return Encode(int(lval / rval)); else return Encode(double(lval) / rval); -- cgit v1.2.3 From 55d2d4cfc0b5c64b0fd8a80b868673971200f7c4 Mon Sep 17 00:00:00 2001 From: Ville Voutilainen Date: Wed, 7 Jun 2017 17:14:03 +0300 Subject: Fix gcc5 miscompile of qle_bitfield Task-number: QTBUG-61123 Change-Id: I124d925846a2f485f7b69870c5eebc7f7f6b50f7 Reviewed-by: Friedemann Kleint Reviewed-by: Lars Knoll --- src/qml/compiler/compiler.pri | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index 60f548a2b0..d9b985e33b 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -52,3 +52,7 @@ qmldevtools_build|qtConfig(qml-interpreter) { $$PWD/qv4instr_moth.cpp \ $$PWD/qv4isel_moth.cpp } + +gcc { + equals(QT_GCC_MAJOR_VERSION, 5): QMAKE_CXXFLAGS += -fno-strict-aliasing +} -- cgit v1.2.3 From f4f89858cffa1107af5139dfb1e1d7b16ca3a1a0 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 6 Jun 2017 14:54:37 +0200 Subject: Use GradientStop and rename ShapeLinearGradient to LinearGradient Thus application code becomes: ShapePath { ... fillGradient: LinearGradient { ... GradientStop { ... } } } which is even more clean and readable. The duplication for stops is now avoided. Change-Id: I50ae2f388e21683a37dc4787763dc71e16eef4f5 Reviewed-by: Mitch Curtis Reviewed-by: J-P Nurmi --- src/imports/shapes/plugin.cpp | 3 +- src/imports/shapes/plugins.qmltypes | 10 +- src/imports/shapes/qquickshape.cpp | 166 +++------------------ src/imports/shapes/qquickshape_p.h | 36 +---- src/imports/shapes/qquickshapegenericrenderer.cpp | 2 +- src/imports/shapes/qquickshapenvprrenderer.cpp | 2 +- src/imports/shapes/qquickshapesoftwarerenderer.cpp | 2 +- 7 files changed, 30 insertions(+), 191 deletions(-) (limited to 'src') diff --git a/src/imports/shapes/plugin.cpp b/src/imports/shapes/plugin.cpp index 2f2f8c74d3..ae0d02da93 100644 --- a/src/imports/shapes/plugin.cpp +++ b/src/imports/shapes/plugin.cpp @@ -63,9 +63,8 @@ public: Q_ASSERT(QByteArray(uri) == QByteArray("QtQuick.Shapes")); qmlRegisterType(uri, 1, 0, "Shape"); qmlRegisterType(uri, 1, 0, "ShapePath"); - qmlRegisterType(uri, 1, 0, "ShapeGradientStop"); qmlRegisterUncreatableType(uri, 1, 0, "ShapeGradient", QQuickShapeGradient::tr("ShapeGradient is an abstract base class")); - qmlRegisterType(uri, 1, 0, "ShapeLinearGradient"); + qmlRegisterType(uri, 1, 0, "LinearGradient"); } }; diff --git a/src/imports/shapes/plugins.qmltypes b/src/imports/shapes/plugins.qmltypes index 28d8dd1f12..00d0050085 100644 --- a/src/imports/shapes/plugins.qmltypes +++ b/src/imports/shapes/plugins.qmltypes @@ -191,14 +191,6 @@ Module { Property { name: "spread"; type: "SpreadMode" } Signal { name: "updated" } } - Component { - name: "QQuickShapeGradientStop" - prototype: "QObject" - exports: ["QtQuick.Shapes/ShapeGradientStop 1.0"] - exportMetaObjectRevisions: [0] - Property { name: "position"; type: "double" } - Property { name: "color"; type: "QColor" } - } Component { name: "QQuickShape" defaultProperty: "elements" @@ -232,7 +224,7 @@ Module { name: "QQuickShapeLinearGradient" defaultProperty: "stops" prototype: "QQuickShapeGradient" - exports: ["QtQuick.Shapes/ShapeLinearGradient 1.0"] + exports: ["QtQuick.Shapes/LinearGradient 1.0"] exportMetaObjectRevisions: [0] Property { name: "x1"; type: "double" } Property { name: "y1"; type: "double" } diff --git a/src/imports/shapes/qquickshape.cpp b/src/imports/shapes/qquickshape.cpp index 81736fcd82..3c253eabd4 100644 --- a/src/imports/shapes/qquickshape.cpp +++ b/src/imports/shapes/qquickshape.cpp @@ -47,12 +47,6 @@ #include #include -#include -#include -#include -#include -#include - QT_BEGIN_NAMESPACE /*! @@ -497,6 +491,9 @@ void QQuickShapePath::setDashPattern(const QVector &array) When set, ShapePath.fillColor is ignored and filling is done using one of the ShapeGradient subtypes. + + \note The Gradient type cannot be used here. Rather, prefer using one of the + advanced subtypes, like LinearGradient. */ QQuickShapeGradient *QQuickShapePath::fillGradient() const @@ -570,14 +567,14 @@ void QQuickShapePath::resetFillGradient() ShapePath { strokeWidth: 4 strokeColor: "red" - fillGradient: ShapeLinearGradient { + fillGradient: LinearGradient { x1: 20; y1: 20 x2: 180; y2: 130 - ShapeGradientStop { position: 0; color: "blue" } - ShapeGradientStop { position: 0.2; color: "green" } - ShapeGradientStop { position: 0.4; color: "red" } - ShapeGradientStop { position: 0.6; color: "yellow" } - ShapeGradientStop { position: 1; color: "cyan" } + GradientStop { position: 0; color: "blue" } + GradientStop { position: 0.2; color: "green" } + GradientStop { position: 0.4; color: "red" } + GradientStop { position: 0.6; color: "yellow" } + GradientStop { position: 1; color: "cyan" } } strokeStyle: ShapePath.DashLine dashPattern: [ 1, 4 ] @@ -1062,149 +1059,29 @@ void QQuickShapePrivate::sync() // ***** gradient support ***** -/*! - \qmltype ShapeGradientStop - \instantiates QQuickShapeGradientStop - \inqmlmodule QtQuick.Shapes - \ingroup qtquick-paths - \ingroup qtquick-views - \inherits Object - \brief Defines a color at a position in a gradient - \since 5.10 - */ - -QQuickShapeGradientStop::QQuickShapeGradientStop(QObject *parent) - : QObject(parent), - m_position(0), - m_color(Qt::black) -{ -} - -/*! - \qmlproperty real QtQuick.Shapes::ShapeGradientStop::position - - The position and color properties describe the color used at a given - position in a gradient, as represented by a gradient stop. - - The default value is 0. - */ - -qreal QQuickShapeGradientStop::position() const -{ - return m_position; -} - -void QQuickShapeGradientStop::setPosition(qreal position) -{ - if (m_position != position) { - m_position = position; - if (QQuickShapeGradient *grad = qobject_cast(parent())) - emit grad->updated(); - } -} - -/*! - \qmlproperty real QtQuick.Shapes::ShapeGradientStop::color - - The position and color properties describe the color used at a given - position in a gradient, as represented by a gradient stop. - - The default value is \c black. - */ - -QColor QQuickShapeGradientStop::color() const -{ - return m_color; -} - -void QQuickShapeGradientStop::setColor(const QColor &color) -{ - if (m_color != color) { - m_color = color; - if (QQuickShapeGradient *grad = qobject_cast(parent())) - emit grad->updated(); - } -} - /*! \qmltype ShapeGradient \instantiates QQuickShapeGradient \inqmlmodule QtQuick.Shapes \ingroup qtquick-paths \ingroup qtquick-views - \inherits Object + \inherits Gradient \brief Base type of Shape fill gradients \since 5.10 - This is an abstract base class for gradients like ShapeLinearGradient and - cannot be created directly. + This is an abstract base class for gradients like LinearGradient and + cannot be created directly. It extends \l Gradient with properties like the + spread mode. */ QQuickShapeGradient::QQuickShapeGradient(QObject *parent) - : QObject(parent), + : QQuickGradient(parent), m_spread(PadSpread) { } -int QQuickShapeGradient::countStops(QQmlListProperty *list) -{ - QQuickShapeGradient *grad = qobject_cast(list->object); - Q_ASSERT(grad); - return grad->m_stops.count(); -} - -QObject *QQuickShapeGradient::atStop(QQmlListProperty *list, int index) -{ - QQuickShapeGradient *grad = qobject_cast(list->object); - Q_ASSERT(grad); - return grad->m_stops.at(index); -} - -void QQuickShapeGradient::appendStop(QQmlListProperty *list, QObject *stop) -{ - QQuickShapeGradientStop *sstop = qobject_cast(stop); - if (!sstop) { - qWarning("Gradient stop list only supports QQuickShapeGradientStop elements"); - return; - } - QQuickShapeGradient *grad = qobject_cast(list->object); - Q_ASSERT(grad); - sstop->setParent(grad); - grad->m_stops.append(sstop); -} - /*! - \qmlproperty list QtQuick.Shapes::ShapeGradient::stops - \default - - The list of ShapeGradientStop objects defining the colors at given positions - in the gradient. - */ - -QQmlListProperty QQuickShapeGradient::stops() -{ - return QQmlListProperty(this, nullptr, - &QQuickShapeGradient::appendStop, - &QQuickShapeGradient::countStops, - &QQuickShapeGradient::atStop, - nullptr); -} - -QGradientStops QQuickShapeGradient::sortedGradientStops() const -{ - QGradientStops result; - for (int i = 0; i < m_stops.count(); ++i) { - QQuickShapeGradientStop *s = static_cast(m_stops[i]); - int j = 0; - while (j < result.count() && result[j].first < s->position()) - ++j; - result.insert(j, QGradientStop(s->position(), s->color())); - } - return result; -} - -/*! - \qmlproperty enumeration QtQuick.Shapes::ShapeGradient::spred + \qmlproperty enumeration QtQuick.Shapes::ShapeGradient::spread Specifies how the area outside the gradient area should be filled. The default value is ShapeGradient.PadSpread. @@ -1231,7 +1108,7 @@ void QQuickShapeGradient::setSpread(SpreadMode mode) } /*! - \qmltype ShapeLinearGradient + \qmltype LinearGradient \instantiates QQuickShapeLinearGradient \inqmlmodule QtQuick.Shapes \ingroup qtquick-paths @@ -1244,6 +1121,9 @@ void QQuickShapeGradient::setSpread(SpreadMode mode) these points the gradient is either padded, reflected or repeated depending on the spread type. + \note LinearGradient is not compatible with Rectangle items that only + support Gradient. This type is to be used with Shape. + \sa QLinearGradient */ @@ -1253,10 +1133,10 @@ QQuickShapeLinearGradient::QQuickShapeLinearGradient(QObject *parent) } /*! - \qmlproperty real QtQuick.Shapes::ShapeLinearGradient::x1 - \qmlproperty real QtQuick.Shapes::ShapeLinearGradient::y1 - \qmlproperty real QtQuick.Shapes::ShapeLinearGradient::x2 - \qmlproperty real QtQuick.Shapes::ShapeLinearGradient::y2 + \qmlproperty real QtQuick.Shapes::LinearGradient::x1 + \qmlproperty real QtQuick.Shapes::LinearGradient::y1 + \qmlproperty real QtQuick.Shapes::LinearGradient::x2 + \qmlproperty real QtQuick.Shapes::LinearGradient::y2 These properties define the start and end points between which color interpolation occurs. By default both the stard and end points are set to diff --git a/src/imports/shapes/qquickshape_p.h b/src/imports/shapes/qquickshape_p.h index a01c36e971..50b242e492 100644 --- a/src/imports/shapes/qquickshape_p.h +++ b/src/imports/shapes/qquickshape_p.h @@ -55,38 +55,16 @@ #include #include -#include -#include +#include QT_BEGIN_NAMESPACE class QQuickShapePathPrivate; class QQuickShapePrivate; -class QQuickShapeGradientStop : public QObject +class QQuickShapeGradient : public QQuickGradient { Q_OBJECT - Q_PROPERTY(qreal position READ position WRITE setPosition) - Q_PROPERTY(QColor color READ color WRITE setColor) - -public: - QQuickShapeGradientStop(QObject *parent = nullptr); - - qreal position() const; - void setPosition(qreal position); - - QColor color() const; - void setColor(const QColor &color); - -private: - qreal m_position; - QColor m_color; -}; - -class QQuickShapeGradient : public QObject -{ - Q_OBJECT - Q_PROPERTY(QQmlListProperty stops READ stops) Q_PROPERTY(SpreadMode spread READ spread WRITE setSpread NOTIFY spreadChanged) Q_CLASSINFO("DefaultProperty", "stops") @@ -100,23 +78,13 @@ public: QQuickShapeGradient(QObject *parent = nullptr); - QQmlListProperty stops(); - - QGradientStops sortedGradientStops() const; - SpreadMode spread() const; void setSpread(SpreadMode mode); signals: - void updated(); void spreadChanged(); private: - static int countStops(QQmlListProperty *list); - static QObject *atStop(QQmlListProperty *list, int index); - static void appendStop(QQmlListProperty *list, QObject *stop); - - QVector m_stops; SpreadMode m_spread; }; diff --git a/src/imports/shapes/qquickshapegenericrenderer.cpp b/src/imports/shapes/qquickshapegenericrenderer.cpp index ff226959eb..47203698d5 100644 --- a/src/imports/shapes/qquickshapegenericrenderer.cpp +++ b/src/imports/shapes/qquickshapegenericrenderer.cpp @@ -247,7 +247,7 @@ void QQuickShapeGenericRenderer::setFillGradient(int index, QQuickShapeGradient ShapePathData &d(m_sp[index]); d.fillGradientActive = gradient != nullptr; if (gradient) { - d.fillGradient.stops = gradient->sortedGradientStops(); + d.fillGradient.stops = gradient->gradientStops(); // sorted d.fillGradient.spread = gradient->spread(); if (QQuickShapeLinearGradient *g = qobject_cast(gradient)) { d.fillGradient.start = QPointF(g->x1(), g->y1()); diff --git a/src/imports/shapes/qquickshapenvprrenderer.cpp b/src/imports/shapes/qquickshapenvprrenderer.cpp index a3e9d31be5..f3f8d807ec 100644 --- a/src/imports/shapes/qquickshapenvprrenderer.cpp +++ b/src/imports/shapes/qquickshapenvprrenderer.cpp @@ -135,7 +135,7 @@ void QQuickShapeNvprRenderer::setFillGradient(int index, QQuickShapeGradient *gr ShapePathGuiData &d(m_sp[index]); d.fillGradientActive = gradient != nullptr; if (gradient) { - d.fillGradient.stops = gradient->sortedGradientStops(); + d.fillGradient.stops = gradient->gradientStops(); // sorted d.fillGradient.spread = gradient->spread(); if (QQuickShapeLinearGradient *g = qobject_cast(gradient)) { d.fillGradient.start = QPointF(g->x1(), g->y1()); diff --git a/src/imports/shapes/qquickshapesoftwarerenderer.cpp b/src/imports/shapes/qquickshapesoftwarerenderer.cpp index 33d80be22c..b3373106af 100644 --- a/src/imports/shapes/qquickshapesoftwarerenderer.cpp +++ b/src/imports/shapes/qquickshapesoftwarerenderer.cpp @@ -144,7 +144,7 @@ void QQuickShapeSoftwareRenderer::setFillGradient(int index, QQuickShapeGradient if (QQuickShapeLinearGradient *linearGradient = qobject_cast(gradient)) { QLinearGradient painterGradient(linearGradient->x1(), linearGradient->y1(), linearGradient->x2(), linearGradient->y2()); - painterGradient.setStops(linearGradient->sortedGradientStops()); + painterGradient.setStops(linearGradient->gradientStops()); // sorted switch (gradient->spread()) { case QQuickShapeGradient::PadSpread: painterGradient.setSpread(QGradient::PadSpread); -- cgit v1.2.3 From 123f698c5bbf23ad816bf2274620ab4f0a82ed2f Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 6 Jun 2017 15:51:14 +0200 Subject: Allow freely mixing non-ShapePath objects in Shape The own list property must be kept. However, we can reuse QQuickItemPrivate's data accessors in order to start supporting code like: Shape { .. ShapePath { ... } ShapePath { ... } Rectangle { ... } Image { ... } // any other visual type // or non-visual: Timer { ... } } Change-Id: I6d502d697cae37bf16857770273a749cee1b3aa3 Reviewed-by: J-P Nurmi --- src/imports/shapes/qquickshape.cpp | 52 +++++++++++++++++++------------------- src/imports/shapes/qquickshape_p.h | 6 ++--- 2 files changed, 29 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/imports/shapes/qquickshape.cpp b/src/imports/shapes/qquickshape.cpp index 3c253eabd4..0d060242b4 100644 --- a/src/imports/shapes/qquickshape.cpp +++ b/src/imports/shapes/qquickshape.cpp @@ -588,6 +588,11 @@ void QQuickShapePath::resetFillGradient() \image pathitem-code-example.png + Like \l Item, Shape also allows any visual or non-visual objects to be + declared as children. ShapePath objects are handled specially. This is + useful since it allows adding visual items, like \l Rectangle or \l Image, + and non-visual objects, like \l Timer directly as children of Shape. + \note It is important to be aware of performance implications, in particular when the application is running on the generic Shape implementation due to not having support for accelerated path rendering. The geometry generation @@ -788,31 +793,23 @@ QQuickShape::Status QQuickShape::status() const return d->status; } -static QQuickShapePath *vpe_at(QQmlListProperty *property, int index) -{ - QQuickShapePrivate *d = QQuickShapePrivate::get(static_cast(property->object)); - return d->qmlData.sp.at(index); -} - -static void vpe_append(QQmlListProperty *property, QQuickShapePath *obj) +static void vpe_append(QQmlListProperty *property, QObject *obj) { QQuickShape *item = static_cast(property->object); QQuickShapePrivate *d = QQuickShapePrivate::get(item); - d->qmlData.sp.append(obj); + QQuickShapePath *path = qobject_cast(obj); + if (path) + d->qmlData.sp.append(path); + + QQuickItemPrivate::data_append(property, obj); - if (d->componentComplete) { - QObject::connect(obj, SIGNAL(shapePathChanged()), item, SLOT(_q_shapePathChanged())); + if (path && d->componentComplete) { + QObject::connect(path, SIGNAL(shapePathChanged()), item, SLOT(_q_shapePathChanged())); d->_q_shapePathChanged(); } } -static int vpe_count(QQmlListProperty *property) -{ - QQuickShapePrivate *d = QQuickShapePrivate::get(static_cast(property->object)); - return d->qmlData.sp.count(); -} - -static void vpe_clear(QQmlListProperty *property) +static void vpe_clear(QQmlListProperty *property) { QQuickShape *item = static_cast(property->object); QQuickShapePrivate *d = QQuickShapePrivate::get(item); @@ -822,27 +819,30 @@ static void vpe_clear(QQmlListProperty *property) d->qmlData.sp.clear(); + QQuickItemPrivate::data_clear(property); + if (d->componentComplete) d->_q_shapePathChanged(); } /*! - \qmlproperty list QtQuick.Shapes::Shape::elements + \qmlproperty list QtQuick.Shapes::Shape::data This property holds the ShapePath objects that define the contents of the - Shape. + Shape. It can also contain any other type of objects, since Shape, like Item, + allows adding any visual or non-visual objects as children. \default */ -QQmlListProperty QQuickShape::elements() +QQmlListProperty QQuickShape::data() { - return QQmlListProperty(this, - nullptr, - vpe_append, - vpe_count, - vpe_at, - vpe_clear); + return QQmlListProperty(this, + nullptr, + vpe_append, + QQuickItemPrivate::data_count, + QQuickItemPrivate::data_at, + vpe_clear); } void QQuickShape::classBegin() diff --git a/src/imports/shapes/qquickshape_p.h b/src/imports/shapes/qquickshape_p.h index 50b242e492..c5096be229 100644 --- a/src/imports/shapes/qquickshape_p.h +++ b/src/imports/shapes/qquickshape_p.h @@ -227,8 +227,8 @@ class QQuickShape : public QQuickItem Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) Q_PROPERTY(bool enableVendorExtensions READ enableVendorExtensions WRITE setEnableVendorExtensions NOTIFY enableVendorExtensionsChanged) Q_PROPERTY(Status status READ status NOTIFY statusChanged) - Q_PROPERTY(QQmlListProperty elements READ elements) - Q_CLASSINFO("DefaultProperty", "elements") + Q_PROPERTY(QQmlListProperty data READ data) + Q_CLASSINFO("DefaultProperty", "data") public: enum RendererType { @@ -259,7 +259,7 @@ public: Status status() const; - QQmlListProperty elements(); + QQmlListProperty data(); protected: QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override; -- cgit v1.2.3 From 4ea6d985fa76786a965443f814f3f9b59620e056 Mon Sep 17 00:00:00 2001 From: Venugopal Shivashankar Date: Tue, 7 Feb 2017 15:25:08 +0100 Subject: Doc: Simplify documentation about integrating QML and C++ Change-Id: If110d02aad991646054ee6e522549c07f00946e1 Reviewed-by: Mitch Curtis --- src/qml/doc/images/cppintegration-ex.png | Bin 0 -> 1777 bytes src/qml/doc/snippets/code/backend/backend.cpp | 70 +++++++++++++++++++++++ src/qml/doc/snippets/code/backend/backend.h | 75 ++++++++++++++++++++++++ src/qml/doc/snippets/code/backend/main.cpp | 66 +++++++++++++++++++++ src/qml/doc/snippets/code/backend/main.qml | 79 ++++++++++++++++++++++++++ src/qml/doc/src/cppintegration/topic.qdoc | 63 +++++++++++++++++++- 6 files changed, 352 insertions(+), 1 deletion(-) create mode 100644 src/qml/doc/images/cppintegration-ex.png create mode 100644 src/qml/doc/snippets/code/backend/backend.cpp create mode 100644 src/qml/doc/snippets/code/backend/backend.h create mode 100644 src/qml/doc/snippets/code/backend/main.cpp create mode 100644 src/qml/doc/snippets/code/backend/main.qml (limited to 'src') diff --git a/src/qml/doc/images/cppintegration-ex.png b/src/qml/doc/images/cppintegration-ex.png new file mode 100644 index 0000000000..0b476ccb93 Binary files /dev/null and b/src/qml/doc/images/cppintegration-ex.png differ diff --git a/src/qml/doc/snippets/code/backend/backend.cpp b/src/qml/doc/snippets/code/backend/backend.cpp new file mode 100644 index 0000000000..4a7ee89cec --- /dev/null +++ b/src/qml/doc/snippets/code/backend/backend.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "backend.h" + +BackEnd::BackEnd(QObject *parent) : + QObject(parent) +{ +} + +QString BackEnd::userName() +{ + return m_userName; +} + +void BackEnd::setUserName(const QString &userName) +{ + if (userName == m_userName) + return; + + m_userName = userName; + emit userNameChanged(); +} diff --git a/src/qml/doc/snippets/code/backend/backend.h b/src/qml/doc/snippets/code/backend/backend.h new file mode 100644 index 0000000000..91bb766e1f --- /dev/null +++ b/src/qml/doc/snippets/code/backend/backend.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BACKEND_H +#define BACKEND_H + +#include +#include + +class BackEnd : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString userName READ userName WRITE setUserName NOTIFY userNameChanged) + +public: + explicit BackEnd(QObject *parent = nullptr); + + QString userName(); + void setUserName(const QString &userName); + +signals: + void userNameChanged(); + +private: + QString m_userName; +}; + +#endif // BACKEND_H diff --git a/src/qml/doc/snippets/code/backend/main.cpp b/src/qml/doc/snippets/code/backend/main.cpp new file mode 100644 index 0000000000..d7a1bcbd4f --- /dev/null +++ b/src/qml/doc/snippets/code/backend/main.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "backend.h" + +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); + + qmlRegisterType("io.qt.examples.backend", 1, 0, "BackEnd"); + + QQmlApplicationEngine engine; + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + + return app.exec(); +} diff --git a/src/qml/doc/snippets/code/backend/main.qml b/src/qml/doc/snippets/code/backend/main.qml new file mode 100644 index 0000000000..3720da8412 --- /dev/null +++ b/src/qml/doc/snippets/code/backend/main.qml @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Controls 2.0 +//![import] +import io.qt.examples.backend 1.0 +//![import] + +ApplicationWindow { + id: root + width: 300 + height: 480 + visible: true + +//![backend] + BackEnd { + id: backend + } +//![backend] + +//![username_input] + TextField { + text: backend.userName + placeholderText: qsTr("User name") + anchors.centerIn: parent + + onTextChanged: backend.userName = text + } +//![username_input] +} + diff --git a/src/qml/doc/src/cppintegration/topic.qdoc b/src/qml/doc/src/cppintegration/topic.qdoc index 1aa3bb6ab5..22115395b1 100644 --- a/src/qml/doc/src/cppintegration/topic.qdoc +++ b/src/qml/doc/src/cppintegration/topic.qdoc @@ -27,7 +27,68 @@ /*! \page qtqml-cppintegration-topic.html \title Integrating QML and C++ -\brief Description of how to integrate QML and C++ code +\brief Provides instruction to integrate QML and C++ + +QML applications often need to handle more advanced and performance-intensive +tasks in C++. The most common and quickest way to do this is to expose the C++ +class to the QML runtime, provided the C++ implementation is derived from +QObject. Assuming that you have Qt 5.7 or later installed, the following +step-by-step instructions guide you through the process of using the C++ class, +BackEnd, in a QML application: + +\list 1 + +\li Create a new project using the "Qt Quick Application" template in Qt Creator + +\note Uncheck the \uicontrol {With ui.qml file} option in the +\uicontrol {Define Project Details} section of \uicontrol {New Project Wizard}. + +\li Add a new C++ class called \c BackEnd to the project and replace its header +file contents with: + +\quotefile code/backend/backend.h + +The \c Q_PROPERTY macro declares a property that could be accessed from QML. + +\li Replace its C++ file contents with: + +\quotefile code/backend/backend.cpp + +The \c setUserName function emits the \c userNameChanged signal every time +\c m_userName value changes. The signal can be handled from QML using the +\c onUserNameChanged handler. + +\li Include \c "backend.h" in \c main.cpp and register the class as a QML type +under a import URL as shown below: + +\quotefile code/backend/main.cpp + +The BackEnd class is registered as a type, which is accessible from QML by +importing the URL, "\c{io.qt.examples.backend 1.0}". + +\li Replace the contents of \c main.qml with the following code: + +\quotefile code/backend/main.qml + +The \c BackEnd instance lets you access the \c userName property, which +is updated when the TextField's \c text property changes. + +\endlist + +Now the application can be run. + +\borderedimage cppintegration-ex.png +\caption Application running on Ubuntu + +Qt offers several methods to integrate C++ with QML, and the method discussed +in this tutorial is just one of them. For more details about these methods, +refer to \l{Overview - QML and C++ Integration}. +*/ + +/*! +\page qtqml-cppintegration-overview.html +\title Overview - QML and C++ Integration +\brief Highlights important points about integrating C++ with QML. QML is designed to be easily extensible through C++ code. The classes in the \l {Qt QML} module enable QML objects to be loaded and manipulated from C++, and the nature of QML engine's -- cgit v1.2.3 From 5efbd7165d6867aa376f23c20edcfe49e80518c6 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 11 May 2017 10:09:12 +0200 Subject: Change temp allocation when generating IR For functions that won't get optimized, it's useful to limit the number of temporaries as much as possible. Change-Id: I6e9be3129c064fdc4c01e1ec6f1617e901c05935 Reviewed-by: Erik Verbruggen Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlirbuilder.cpp | 3 +- src/qml/compiler/qv4codegen.cpp | 172 ++++++++++++++++++++++++++----------- src/qml/compiler/qv4codegen_p.h | 12 ++- src/qml/compiler/qv4jsir_p.h | 18 +++- src/qml/compiler/qv4ssa.cpp | 6 +- 5 files changed, 155 insertions(+), 56 deletions(-) (limited to 'src') diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 16eee50cf9..57cb4c607c 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1948,7 +1948,8 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int QQmlTypeNameCache::Result r = imports->query(name); if (r.isValid()) { if (r.scriptIndex != -1) { - return subscript(_block->TEMP(_importedScriptsTemp), _block->CONST(QV4::IR::SInt32Type, r.scriptIndex)); + return _block->SUBSCRIPT(_block->TEMP(_importedScriptsTemp), + _block->CONST(QV4::IR::SInt32Type, r.scriptIndex)); } else if (r.type) { QV4::IR::Name *typeName = _block->NAME(name, line, col); // Make sure the run-time loads this through the more efficient singleton getter. diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index fcfbdfa74b..1e98d1167b 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -113,6 +113,18 @@ static inline void setJumpOutLocation(IR::Stmt *s, const Statement *body, } } +static inline bool isSimpleExpr(IR::Expr *e) +{ + switch (e->exprKind) { + case IR::Expr::TempExpr: + case IR::Expr::ArgLocalExpr: + case IR::Expr::ConstExpr: + return true; + default: + return false; + } +} + Codegen::ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode) : _cg(cg) , _sourceCode(sourceCode) @@ -561,28 +573,6 @@ IR::Expr *Codegen::member(IR::Expr *base, const QString *name) } } -IR::Expr *Codegen::subscript(IR::Expr *base, IR::Expr *index) -{ - if (hasError) - return 0; - - if (! base->asTemp() && !base->asArgLocal()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), base); - base = _block->TEMP(t); - } - - if (! index->asTemp() && !index->asArgLocal() && !index->asConst()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), index); - index = _block->TEMP(t); - } - - Q_ASSERT(base->asTemp() || base->asArgLocal()); - Q_ASSERT(index->asTemp() || index->asArgLocal() || index->asConst()); - return _block->SUBSCRIPT(base, index); -} - IR::Expr *Codegen::argument(IR::Expr *expr) { if (expr && !expr->asTemp()) { @@ -635,12 +625,14 @@ IR::Expr *Codegen::unop(IR::AluOp op, IR::Expr *expr, const SourceLocation &loc) } } } - if (!expr->asTemp() && !expr->asArgLocal()) { - const unsigned t = _block->newTemp(); - setLocation(move(_block->TEMP(t), expr), loc); - expr = _block->TEMP(t); - } - Q_ASSERT(expr->asTemp() || expr->asArgLocal()); + + TempScope scope(_function); + if (isSimpleExpr(expr)) + return _block->UNOP(op, expr); + + const unsigned t = _block->newTemp(); + setLocation(move(_block->TEMP(t), expr), loc); + expr = _block->TEMP(t); return _block->UNOP(op, expr); } @@ -649,6 +641,8 @@ IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right, const AS if (hasError) return 0; + TempScope scope(_function); + if (IR::Const *c1 = left->asConst()) { if (IR::Const *c2 = right->asConst()) { if ((c1->type & IR::NumberType) && (c2->type & IR::NumberType)) { @@ -736,6 +730,8 @@ IR::Stmt *Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op) return move(target, binop(op, target, source)); } + TempScope scope(_function); + if (!source->asTemp() && !source->asConst() && !target->asTemp() && !source->asArgLocal() && !target->asArgLocal()) { unsigned t = _block->newTemp(); _block->MOVE(_block->TEMP(t), source); @@ -755,6 +751,8 @@ IR::Stmt *Codegen::cjump(IR::Expr *cond, IR::BasicBlock *iftrue, IR::BasicBlock if (hasError) return 0; + TempScope scope(_function); + if (! (cond->asTemp() || (cond->asBinop() && cjumpCanHandle(cond->asBinop()->op)) )) { const unsigned t = _block->newTemp(); move(_block->TEMP(t), cond); @@ -774,12 +772,16 @@ void Codegen::accept(Node *node) void Codegen::statement(Statement *ast) { + TempScope scope(_function); + _block->nextLocation = ast->firstSourceLocation(); accept(ast); } void Codegen::statement(ExpressionNode *ast) { + TempScope scope(_function); + if (! ast) { return; } else { @@ -888,6 +890,7 @@ void Codegen::variableDeclaration(VariableDeclaration *ast) if (lhs->asArgLocal()) { move(lhs, initializer); } else { + TempScope scope(_function); int initialized = _block->newTemp(); move(_block->TEMP(initialized), initializer); move(lhs, _block->TEMP(initialized)); @@ -1091,6 +1094,10 @@ bool Codegen::visit(ArrayLiteral *ast) if (hasError) return false; + const unsigned t = _block->newTemp(); + + TempScope scope(_function); + IR::ExprList *args = 0; IR::ExprList *current = 0; for (ElementList *it = ast->elements; it; it = it->next) { @@ -1136,7 +1143,6 @@ bool Codegen::visit(ArrayLiteral *ast) current->expr = _block->CONST(IR::MissingType, 0); } - const unsigned t = _block->newTemp(); move(_block->TEMP(t), _block->CALL(_block->NAME(IR::Name::builtin_define_array, 0, 0), args)); _expr.code = _block->TEMP(t); return false; @@ -1147,11 +1153,25 @@ bool Codegen::visit(ArrayMemberExpression *ast) if (hasError) return false; - Result base = expression(ast->base); - Result index = expression(ast->expression); + IR::Expr *base = *expression(ast->base); + if (hasError) + return false; + if (!isSimpleExpr(base)) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), base); + base = _block->TEMP(t); + } + + IR::Expr *index = *expression(ast->expression); if (hasError) return false; - _expr.code = subscript(*base, *index); + if (!isSimpleExpr(index)) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), index); + index = _block->TEMP(t); + } + + _expr.code = _block->SUBSCRIPT(base, index); return false; } @@ -1287,14 +1307,12 @@ bool Codegen::visit(BinaryExpression *ast) return false; } - if (_expr.accept(nx)) { - move(left, *right, baseOp(ast->op)); - } else { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), *right); - move(left, _block->TEMP(t), baseOp(ast->op)); + TempScope scope(_function); + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), *right); + move(left, _block->TEMP(t), baseOp(ast->op)); + if (!_expr.accept(nx)) _expr.code = left; - } break; } @@ -1308,6 +1326,7 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::Lt: case QSOperator::StrictEqual: case QSOperator::StrictNotEqual: { + TempScope scope(_function); if (!left->asTemp() && !left->asArgLocal() && !left->asConst()) { const unsigned t = _block->newTemp(); setLocation(move(_block->TEMP(t), left), ast->operatorToken); @@ -1337,6 +1356,7 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::RShift: case QSOperator::Sub: case QSOperator::URShift: { + TempScope scope(_function); if (!left->asTemp() && !left->asArgLocal() && !left->asConst()) { const unsigned t = _block->newTemp(); setLocation(move(_block->TEMP(t), left), ast->operatorToken); @@ -1388,6 +1408,7 @@ bool Codegen::visit(ConditionalExpression *ast) IR::BasicBlock *endif = _function->newBasicBlock(exceptionHandler()); const unsigned t = _block->newTemp(); + TempScope scope(_function); condition(ast->expression, iftrue, iffalse); @@ -1491,6 +1512,8 @@ bool Codegen::visit(FunctionExpression *ast) if (hasError) return false; + TempScope scope(_function); + int function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); _expr.code = _block->CLOSURE(function); return false; @@ -1571,6 +1594,7 @@ bool Codegen::visit(NewExpression *ast) { if (hasError) return false; + TempScope scope(_function); Result base = expression(ast->expression); if (hasError) @@ -1590,6 +1614,10 @@ bool Codegen::visit(NewMemberExpression *ast) if (hasError) return false; + const unsigned t = _block->newTemp(); + + TempScope scope(_function); + Result base = expression(ast->base); if (hasError) return false; @@ -1610,7 +1638,6 @@ bool Codegen::visit(NewMemberExpression *ast) (*args_it)->init(actual); args_it = &(*args_it)->next; } - const unsigned t = _block->newTemp(); move(_block->TEMP(t), _block->NEW(expr, args)); _expr.code = _block->TEMP(t); return false; @@ -1621,10 +1648,12 @@ bool Codegen::visit(NotExpression *ast) if (hasError) return false; + const unsigned r = _block->newTemp(); + TempScope scope(_function); + Result expr = expression(ast->expression); if (hasError) return false; - const unsigned r = _block->newTemp(); setLocation(move(_block->TEMP(r), unop(IR::OpNot, *expr, ast->notToken)), ast->notToken); _expr.code = _block->TEMP(r); return false; @@ -1677,6 +1706,9 @@ bool Codegen::visit(ObjectLiteral *ast) QMap valueMap; + const unsigned t = _block->newTemp(); + TempScope scope(_function); + for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { QString name = it->assignment->name->asString(); if (PropertyNameAndValue *nv = AST::cast(it->assignment)) { @@ -1690,7 +1722,13 @@ bool Codegen::visit(ObjectLiteral *ast) return false; } - valueMap[name].value = *value; + if (IR::Const *c = (*value)->asConst()) { + valueMap[name].value = c; + } else { + unsigned t = _block->newTemp(); + move(_block->TEMP(t), *value); + valueMap[name].value = _block->TEMP(t); + } } else if (PropertyGetterSetter *gs = AST::cast(it->assignment)) { const int function = defineFunction(name, gs, gs->formals, gs->functionBody ? gs->functionBody->elements : 0); ObjectPropertyValue &v = valueMap[name]; @@ -1761,12 +1799,9 @@ bool Codegen::visit(ObjectLiteral *ast) current = current->next; current->expr = _block->CONST(IR::BoolType, true); - unsigned value = _block->newTemp(); - move(_block->TEMP(value), it->value); - current->next = _function->New(); current = current->next; - current->expr = _block->TEMP(value); + current->expr = it->value; } else { current->next = _function->New(); current = current->next; @@ -1799,7 +1834,6 @@ bool Codegen::visit(ObjectLiteral *ast) args->next = arrayEntries; } - const unsigned t = _block->newTemp(); move(_block->TEMP(t), _block->CALL(_block->NAME(IR::Name::builtin_define_object_literal, ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), args)); @@ -1825,6 +1859,7 @@ bool Codegen::visit(PostDecrementExpression *ast) const unsigned oldValue = _block->newTemp(); setLocation(move(_block->TEMP(oldValue), unop(IR::OpUPlus, *expr, ast->decrementToken)), ast->decrementToken); + TempScope scope(_function); const unsigned newValue = _block->newTemp(); setLocation(move(_block->TEMP(newValue), binop(IR::OpSub, _block->TEMP(oldValue), _block->CONST(IR::NumberType, 1), ast->decrementToken)), ast->decrementToken); setLocation(move(*expr, _block->TEMP(newValue)), ast->decrementToken); @@ -1853,6 +1888,7 @@ bool Codegen::visit(PostIncrementExpression *ast) const unsigned oldValue = _block->newTemp(); setLocation(move(_block->TEMP(oldValue), unop(IR::OpUPlus, *expr, ast->incrementToken)), ast->incrementToken); + TempScope scope(_function); const unsigned newValue = _block->newTemp(); setLocation(move(_block->TEMP(newValue), binop(IR::OpAdd, _block->TEMP(oldValue), _block->CONST(IR::NumberType, 1), ast->incrementToken)), ast->incrementToken); setLocation(move(*expr, _block->TEMP(newValue)), ast->incrementToken); @@ -1949,10 +1985,12 @@ bool Codegen::visit(TildeExpression *ast) if (hasError) return false; + const unsigned t = _block->newTemp(); + TempScope scope(_function); + Result expr = expression(ast->expression); if (hasError) return false; - const unsigned t = _block->newTemp(); setLocation(move(_block->TEMP(t), unop(IR::OpCompl, *expr, ast->tildeToken)), ast->tildeToken); _expr.code = _block->TEMP(t); return false; @@ -1976,6 +2014,8 @@ bool Codegen::visit(TypeOfExpression *ast) if (hasError) return false; + TempScope scope(_function); + Result expr = expression(ast->expression); if (hasError) return false; @@ -2018,6 +2058,8 @@ bool Codegen::visit(VoidExpression *ast) if (hasError) return false; + TempScope scope(_function); + statement(ast->expression); _expr.code = _block->CONST(IR::UndefinedType, 0); return false; @@ -2028,6 +2070,8 @@ bool Codegen::visit(FunctionDeclaration * ast) if (hasError) return false; + TempScope scope(_function); + if (_env->compilationMode == QmlBinding) move(_block->TEMP(_returnAddress), _block->NAME(ast->name.toString(), 0, 0)); _expr.accept(nx); @@ -2190,6 +2234,8 @@ bool Codegen::visit(Block *ast) if (hasError) return false; + TempScope scope(_function); + for (StatementList *it = ast->statements; it; it = it->next) { statement(it->statement); } @@ -2201,6 +2247,8 @@ bool Codegen::visit(BreakStatement *ast) if (hasError) return false; + TempScope scope(_function); + if (!_loop) { throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop")); return false; @@ -2228,6 +2276,8 @@ bool Codegen::visit(ContinueStatement *ast) if (hasError) return false; + TempScope scope(_function); + Loop *loop = 0; if (ast->label.isEmpty()) { for (loop = _loop; loop; loop = loop->parent) { @@ -2267,6 +2317,8 @@ bool Codegen::visit(DoWhileStatement *ast) if (hasError) return true; + TempScope scope(_function); + IR::BasicBlock *loopbody = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *loopcond = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *loopend = _function->newBasicBlock(exceptionHandler()); @@ -2302,6 +2354,8 @@ bool Codegen::visit(ExpressionStatement *ast) if (hasError) return true; + TempScope scope(_function); + if (_env->compilationMode == EvalCode || _env->compilationMode == QmlBinding) { Result e = expression(ast->expression); if (*e) @@ -2317,6 +2371,8 @@ bool Codegen::visit(ForEachStatement *ast) if (hasError) return true; + TempScope scope(_function); + IR::BasicBlock *foreachin = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *foreachbody = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *foreachend = _function->newBasicBlock(exceptionHandler()); @@ -2363,6 +2419,8 @@ bool Codegen::visit(ForStatement *ast) if (hasError) return true; + TempScope scope(_function); + IR::BasicBlock *forcond = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *forbody = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *forstep = _function->newBasicBlock(exceptionHandler()); @@ -2399,6 +2457,8 @@ bool Codegen::visit(IfStatement *ast) if (hasError) return true; + TempScope scope(_function); + IR::BasicBlock *iftrue = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *iffalse = ast->ko ? _function->newBasicBlock(exceptionHandler()) : 0; IR::BasicBlock *endif = _function->newBasicBlock(exceptionHandler()); @@ -2425,6 +2485,8 @@ bool Codegen::visit(LabelledStatement *ast) if (hasError) return true; + TempScope scope(_function); + // check that no outer loop contains the label Loop *l = _loop; while (l) { @@ -2462,6 +2524,8 @@ bool Codegen::visit(LocalForEachStatement *ast) if (hasError) return true; + TempScope scope(_function); + IR::BasicBlock *foreachin = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *foreachbody = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *foreachend = _function->newBasicBlock(exceptionHandler()); @@ -2502,6 +2566,8 @@ bool Codegen::visit(LocalForStatement *ast) if (hasError) return true; + TempScope scope(_function); + IR::BasicBlock *forcond = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *forbody = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *forstep = _function->newBasicBlock(exceptionHandler()); @@ -2564,6 +2630,8 @@ bool Codegen::visit(SwitchStatement *ast) if (hasError) return true; + TempScope scope(_function); + IR::BasicBlock *switchend = _function->newBasicBlock(exceptionHandler()); if (ast->block) { @@ -2659,6 +2727,8 @@ bool Codegen::visit(ThrowStatement *ast) if (hasError) return true; + TempScope scope(_function); + Result expr = expression(ast->expression); move(_block->TEMP(_returnAddress), *expr); IR::ExprList *throwArgs = _function->New(); @@ -2672,6 +2742,8 @@ bool Codegen::visit(TryStatement *ast) if (hasError) return true; + TempScope scope(_function); + _function->hasTry = true; if (_function->isStrict && ast->catchExpression && @@ -2750,6 +2822,8 @@ bool Codegen::visit(TryStatement *ast) _function->addBasicBlock(finallyBody); _block = finallyBody; + TempScope scope(_function); + int hasException = _block->newTemp(); move(_block->TEMP(hasException), _block->CALL(_block->NAME(IR::Name::builtin_unwind_exception, /*line*/0, /*column*/0), 0)); @@ -2834,6 +2908,8 @@ bool Codegen::visit(WithStatement *ast) if (hasError) return true; + TempScope scope(_function); + _function->hasWith = true; const int withObject = _block->newTemp(); diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 742ee79648..1cbe6949a1 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -231,6 +231,17 @@ protected: } }; + struct TempScope { + TempScope(QV4::IR::Function *f) + : function(f), + tempCountForScope(f->currentTemp) {} + ~TempScope() { + function->currentTemp = tempCountForScope; + } + QV4::IR::Function *function; + int tempCountForScope; + }; + Environment *newEnvironment(AST::Node *node, Environment *parent, CompilationMode compilationMode) { Environment *env = new Environment(parent, compilationMode); @@ -293,7 +304,6 @@ protected: } QV4::IR::Expr *member(QV4::IR::Expr *base, const QString *name); - QV4::IR::Expr *subscript(QV4::IR::Expr *base, QV4::IR::Expr *index); QV4::IR::Expr *argument(QV4::IR::Expr *expr); QV4::IR::Expr *reference(QV4::IR::Expr *expr); QV4::IR::Expr *unop(QV4::IR::AluOp op, QV4::IR::Expr *expr, const AST::SourceLocation &loc = AST::SourceLocation()); diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index caab9c0f9e..6f14e3dfaf 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -1113,7 +1113,11 @@ public: return false; } - unsigned newTemp(); + enum TempForWhom { + NewTempForCodegen, + NewTempForOptimizer + }; + unsigned newTemp(TempForWhom tfw = NewTempForCodegen); Temp *TEMP(unsigned kind); ArgLocal *ARG(unsigned index, unsigned scope); @@ -1278,6 +1282,7 @@ struct Function { Module *module; QQmlJS::MemoryPool *pool; const QString *name; + int currentTemp = 0; int tempCount; int maxNumberOfArguments; QSet strings; @@ -1485,10 +1490,17 @@ protected: BasicBlock *currentBB; }; -inline unsigned BasicBlock::newTemp() +inline unsigned BasicBlock::newTemp(TempForWhom tfw) { Q_ASSERT(!isRemoved()); - return function->tempCount++; + + if (tfw == NewTempForOptimizer) + return function->tempCount++; + + int t = function->currentTemp++; + if (function->tempCount < function->currentTemp) + function->tempCount = function->currentTemp; + return t; } inline Temp *BasicBlock::TEMP(unsigned index) diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp index fc136b09ff..8cf5fac760 100644 --- a/src/qml/compiler/qv4ssa.cpp +++ b/src/qml/compiler/qv4ssa.cpp @@ -2770,7 +2770,7 @@ public: } else if (Const *c = (*conversion.expr)->asConst()) { convertConst(c, conversion.targetType); } else if (ArgLocal *al = (*conversion.expr)->asArgLocal()) { - Temp *target = bb->TEMP(bb->newTemp()); + Temp *target = bb->TEMP(bb->newTemp(BasicBlock::NewTempForOptimizer)); target->type = conversion.targetType; Expr *convert = bb->CONVERT(al, conversion.targetType); Move *convCall = f->NewStmt(); @@ -2791,7 +2791,7 @@ public: *conversion.expr = source; } else if (Temp *t = (*conversion.expr)->asTemp()) { - Temp *target = bb->TEMP(bb->newTemp()); + Temp *target = bb->TEMP(bb->newTemp(BasicBlock::NewTempForOptimizer)); target->type = conversion.targetType; Expr *convert = bb->CONVERT(t, conversion.targetType); Move *convCall = f->NewStmt(); @@ -2820,7 +2820,7 @@ public: // to: // double{%3} = double{-double{%1}}; // int32{%2} = int32{convert(double{%3})}; - Temp *tmp = bb->TEMP(bb->newTemp()); + Temp *tmp = bb->TEMP(bb->newTemp(BasicBlock::NewTempForOptimizer)); tmp->type = u->type; Move *extraMove = f->NewStmt(); worklist.registerNewStatement(extraMove); -- cgit v1.2.3 From 939f07695b853a4da2e237c5f1c3d50e34f9c45c Mon Sep 17 00:00:00 2001 From: Alexander Volkov Date: Thu, 8 Jun 2017 13:48:59 +0300 Subject: Use pre-generated QML caches even if they are not writable For example, if they are installed into system paths. Change-Id: Id14ccfbe57c5bcf273c79510034dc20400073451 Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4compileddata.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index ac4192bc12..485a5e6fb7 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -82,7 +82,7 @@ static QString cacheFilePath(const QUrl &url) { const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url); const QString localCachePath = localSourcePath + QLatin1Char('c'); - if (QFileInfo(QFileInfo(localSourcePath).dir().absolutePath()).isWritable()) + if (QFile::exists(localCachePath) || QFileInfo(QFileInfo(localSourcePath).dir().absolutePath()).isWritable()) return localCachePath; QCryptographicHash fileNameHash(QCryptographicHash::Sha1); fileNameHash.addData(localSourcePath.toUtf8()); -- cgit v1.2.3 From db6f1440cbe78018e442c1fb961310a4e619e8fe Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 9 Jun 2017 23:36:49 +0200 Subject: QQuickItemView: fix releaseItem() loops Calling releaseItem() destroys the item, which emits childrenChanged for the contentItem, and if at that point anything calls setFooMargin(), setContentHeight(), returnToBounds(), or many other methods that indirectly access the visibleItems list, it leads to a crash due to read after free. Add a releaseVisibleItems() helper method that makes a copy, clears the original list first, and then releases the items. Task-number: QTBUG-48394 Task-number: QTBUG-61294 Change-Id: I29e4d3870d33549e8bf789de84c67ab1826fca7d Reviewed-by: Robin Burchell --- src/quick/items/qquickgridview.cpp | 4 +--- src/quick/items/qquickitemview.cpp | 8 ++------ src/quick/items/qquickitemview_p_p.h | 9 +++++++++ src/quick/items/qquicklistview.cpp | 4 +--- 4 files changed, 13 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp index fd78c46a16..c570b95a21 100644 --- a/src/quick/items/qquickgridview.cpp +++ b/src/quick/items/qquickgridview.cpp @@ -495,9 +495,7 @@ bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal // We've jumped more than a page. Estimate which items are now // visible and fill from there. int count = (fillFrom - (rowPos + rowSize())) / (rowSize()) * columns; - for (FxViewItem *item : qAsConst(visibleItems)) - releaseItem(item); - visibleItems.clear(); + releaseVisibleItems(); modelIndex += count; if (modelIndex >= model->count()) modelIndex = model->count() - 1; diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index 555db03962..084b1f197a 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -380,9 +380,7 @@ void QQuickItemView::setDelegate(QQmlComponent *delegate) int oldCount = dataModel->count(); dataModel->setDelegate(delegate); if (isComponentComplete()) { - for (FxViewItem *item : qAsConst(d->visibleItems)) - d->releaseItem(item); - d->visibleItems.clear(); + d->releaseVisibleItems(); d->releaseItem(d->currentItem); d->currentItem = 0; d->updateSectionCriteria(); @@ -1743,9 +1741,7 @@ void QQuickItemViewPrivate::clear() currentChanges.reset(); timeline.clear(); - for (FxViewItem *item : qAsConst(visibleItems)) - releaseItem(item); - visibleItems.clear(); + releaseVisibleItems(); visibleIndex = 0; for (FxViewItem *item : qAsConst(releasePendingTransition)) { diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h index 3087682ac7..b6353246e8 100644 --- a/src/quick/items/qquickitemview_p_p.h +++ b/src/quick/items/qquickitemview_p_p.h @@ -269,6 +269,15 @@ public: q->polish(); } + void releaseVisibleItems() { + // make a copy and clear the visibleItems first to avoid destroyed + // items being accessed during the loop (QTBUG-61294) + const QList oldVisible = visibleItems; + visibleItems.clear(); + for (FxViewItem *item : oldVisible) + releaseItem(item); + } + QPointer model; QVariant modelVariant; int itemCount; diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index f739115e6b..18f9b8512d 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -660,9 +660,7 @@ bool QQuickListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal int newModelIdx = qBound(0, modelIndex + count, model->count()); count = newModelIdx - modelIndex; if (count) { - for (FxViewItem *item : qAsConst(visibleItems)) - releaseItem(item); - visibleItems.clear(); + releaseVisibleItems(); modelIndex = newModelIdx; visibleIndex = modelIndex; visiblePos = itemEnd + count * (averageSize + spacing); -- cgit v1.2.3 From 5f2ec1a8c73fc7b6c20595110c0e04af21469082 Mon Sep 17 00:00:00 2001 From: Nico Vertriest Date: Thu, 12 Jan 2017 12:45:58 +0100 Subject: Doc: add doc and examples to RegExpValidator QML type Change-Id: I0ebc06aec6d2392f613fb4c312c189cc1d90a184 Reviewed-by: Venugopal Shivashankar --- src/quick/doc/snippets/qml/regexp.qml | 46 +++++++++++++++++++++++++++++++++++ src/quick/util/qquickvalidator.cpp | 13 ++++++++++ 2 files changed, 59 insertions(+) create mode 100644 src/quick/doc/snippets/qml/regexp.qml (limited to 'src') diff --git a/src/quick/doc/snippets/qml/regexp.qml b/src/quick/doc/snippets/qml/regexp.qml new file mode 100644 index 0000000000..c30336d418 --- /dev/null +++ b/src/quick/doc/snippets/qml/regexp.qml @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 +//![0] +TextInput { + id: hexNumber + validator: RegExpValidator { regExp: /[0-9A-F]+/ } +} +//![0] diff --git a/src/quick/util/qquickvalidator.cpp b/src/quick/util/qquickvalidator.cpp index a05117bd06..93f414fe80 100644 --- a/src/quick/util/qquickvalidator.cpp +++ b/src/quick/util/qquickvalidator.cpp @@ -219,6 +219,19 @@ void QQuickDoubleValidator::resetLocaleName() matching "a". By default, this property contains a regular expression with the pattern .* that matches any string. + + Below you can find an example of a \l TextInput object with a RegExpValidator specified: + + \snippet qml/regexp.qml 0 + + Some more examples of regular expressions: + + \list + \li A list of numbers with one to three positions separated by a comma: + /\d{1,3}(?:,\d{1,3})+$/ + \li An amount consisting of up to 3 numbers before the decimal point, and + 1 to 2 after the decimal point: \li /(\d{1,3})([.,]\d{1,2})?$/ + \endlist */ #endif // validator -- cgit v1.2.3 From 3479810d0eb44b8fdc1478af6510a7e15541e26e Mon Sep 17 00:00:00 2001 From: Nico Vertriest Date: Thu, 30 Mar 2017 16:13:49 +0200 Subject: Doc: correct link issues qqmlengine.cpp:2182: warning: Can't link to 'LocalStorage.openDatabaseSync()' qqmlengine.cpp:451: warning: Can't link to 'Window.screen' Change-Id: Ib2a3548805125366d7a81982fa2039f6b9341d9b Reviewed-by: Venugopal Shivashankar --- src/qml/qml/qqmlengine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 1f6d1f3b57..f0564e7b33 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -590,7 +590,7 @@ The following functions are also on the Qt object. \li application.font \endlist - \sa Screen, Window, Window.screen + \sa Screen, Window, {QtQuick::Window::screen}{Window.screen} */ /*! -- cgit v1.2.3 From 84275fda5eaf709043572d6bbd76d5714c517af7 Mon Sep 17 00:00:00 2001 From: Nico Vertriest Date: Fri, 13 Jan 2017 16:03:29 +0100 Subject: Doc: add doc and snippet to model/view documentation Change-Id: I64dd815ae377dced7022fed11ab1922c5913e65d Reviewed-by: Edward Welbourne Reviewed-by: Venugopal Shivashankar --- .../doc/src/concepts/modelviewsdata/modelview.qdoc | 59 +++++++++++++++++----- .../doc/src/concepts/positioning/layouts.qdoc | 3 +- 2 files changed, 46 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/quick/doc/src/concepts/modelviewsdata/modelview.qdoc b/src/quick/doc/src/concepts/modelviewsdata/modelview.qdoc index 324fc9750f..47dcd6d98c 100644 --- a/src/quick/doc/src/concepts/modelviewsdata/modelview.qdoc +++ b/src/quick/doc/src/concepts/modelviewsdata/modelview.qdoc @@ -87,6 +87,7 @@ To visualize data, bind the view's \c model property to a model and the The club may decorate the members list by binding visual objects to the \c header and \c footer properties. The visual object may be defined inline, in another file, or in a \l {Component} type. + \snippet qml/listview-decorations.qml decorations \image listview-decorations.png @@ -102,7 +103,6 @@ To visualize data, bind the view's \c model property to a model and the will always ensure that the \c currentIndex is within the highlight range specified. - \section2 ListView Sections \l {ListView} contents may be grouped into \e sections, where related list @@ -195,7 +195,7 @@ To visualize data, bind the view's \c model property to a model and the Positioning of items from a model can be achieved using a \l{Repeater}. - \section2 ListModel + \section2 List Model ListModel is a simple hierarchy of types specified in QML. The available roles are specified by the \l ListElement properties. @@ -222,7 +222,7 @@ To visualize data, bind the view's \c model property to a model and the using the model. To reset the roles available in the model, call ListModel::clear(). - \section2 XmlListModel + \section2 XML Model XmlListModel allows construction of a model from an XML data source. The roles are specified via the \l XmlRole type. The type needs to be imported. @@ -244,23 +244,44 @@ To visualize data, bind the view's \c model property to a model and the } \endqml + The \c query property specifies that the XmlListModel generates a model item + for each \c in the XML document. + The \l{Qt Quick Demo - RSS News}{RSS News demo} shows how XmlListModel can be used to display an RSS feed. - \section2 VisualItemModel + \section2 Object Model + + ObjectModel contains the visual items to be used in a view. When an ObjectModel + is used in a view, the view does not require a delegate because the ObjectModel + already contains the visual delegate (items). - VisualItemModel allows QML items to be provided as a model. + The example below places three colored rectangles in a ListView. - This model contains both the data and delegate; the child items of a - VisualItemModel provide the contents of the delegate. The model - does not provide any roles. + \code + import QtQuick 2.0 + import QtQml.Models 2.1 + + Rectangle { + ObjectModel { + id: itemModel + Rectangle { height: 30; width: 80; color: "red" } + Rectangle { height: 30; width: 80; color: "green" } + Rectangle { height: 30; width: 80; color: "blue" } + } - \snippet qml/models/visual-model-and-view.qml visual model and view + ListView { + anchors.fill: parent + model: itemModel + } + } + \endcode - Note that in the above example there is no delegate required. - The items of the model itself provide the visual types that - will be positioned by the view. + \note VisualItemModel can also be used, but it is only provided for compatibility + reasons. VisualItemModel allows a QML item to be provided as a model. This model + contains both the data and delegate; the child items of a VisualItemModel + provide the contents of the delegate. The model does not provide any roles. \section2 Integers as Models @@ -357,8 +378,18 @@ rectangles for the Grid item to position in a 5 by 5 arrangement. The number of items created by a Repeater is held by its \l{Repeater::}{count} property. It is not possible to set this property to determine the number of items to be created. Instead, as in the above example, we use an integer as -the model. This is explained in the \l{qtquick-modelviewsdata-modelview.html#integers-as-models}{QML Data Models} -document. +the model. + +For more details, see the \l{qtquick-modelviewsdata-modelview.html#integers-as-models}{QML Data Models} document. + +If the model is a string list, the delegate is also exposed to a read-only +\c modelData property that holds the string. For example: + +\table + \row + \li \snippet qml/repeaters/repeater.qml modeldata + \li \image repeater-modeldata.png +\endtable It is also possible to use a delegate as the template for the items created by a Repeater. This is specified using the \l{Repeater::}{delegate} property. diff --git a/src/quick/doc/src/concepts/positioning/layouts.qdoc b/src/quick/doc/src/concepts/positioning/layouts.qdoc index 47ed2563f8..3824d17559 100644 --- a/src/quick/doc/src/concepts/positioning/layouts.qdoc +++ b/src/quick/doc/src/concepts/positioning/layouts.qdoc @@ -133,6 +133,5 @@ control of spacing between items and between lines of items. There are several other ways to position items in a user interface. In addition to the basic technique of specifying their coordinates directly, they can be positioned relative to other items with \l{anchor-layout}{anchors}, or used -with \l{QML Data Models} such as -\l{QML Data Models#VisualItemModel}{VisualItemModel}. +with \l{QML Data Models} such as \l{QML Data Models#Object Model}{Object Model}. */ -- cgit v1.2.3 From 7c27955968bab135b0e0db3a5f707efa86e80464 Mon Sep 17 00:00:00 2001 From: Alexander Volkov Date: Tue, 13 Jun 2017 13:40:10 +0300 Subject: Fix loading QML caches for qrc:/// urls Running examples/quick/window/window produces debug messages: "QML source file has moved to a different location." This is because QQmlFile::urlToLocalFileOrQrc(const QString &) overload is incompatible with QQmlFile::urlToLocalFileOrQrc(const QUrl &) when it deals with qrc:/// urls. For example it returns ":///window/window.qml" while the QUrl overload returns ":/window/window.qml". Thus the comparison of source paths in CompilationUnit::loadFromDisk() fails. Fix the incompatibility and add a test. Change-Id: I20449b8cf13d715d88860f2cd413ab39c893f3ef Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlfile.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/qml/qml/qqmlfile.cpp b/src/qml/qml/qqmlfile.cpp index 4e4db086b0..93c3e8e00c 100644 --- a/src/qml/qml/qqmlfile.cpp +++ b/src/qml/qml/qqmlfile.cpp @@ -603,6 +603,12 @@ empty string. */ QString QQmlFile::urlToLocalFileOrQrc(const QString& url) { + if (url.startsWith(QLatin1String("qrc://"), Qt::CaseInsensitive)) { + if (url.length() > 6) + return QLatin1Char(':') + url.midRef(6); + return QString(); + } + if (url.startsWith(QLatin1String("qrc:"), Qt::CaseInsensitive)) { if (url.length() > 4) return QLatin1Char(':') + url.midRef(4); -- cgit v1.2.3 From 784ea8c09d448a418b3128be8bee14d9535e36c9 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Mon, 12 Jun 2017 17:43:54 +0200 Subject: Silence -Wuninitialized warning Task-number: QTBUG-61089 Change-Id: I8b1fb03d040b04b3b14f371bf1a5ba8c2318054f Reviewed-by: Edward Welbourne Reviewed-by: Thiago Macieira --- src/qml/compiler/qv4instr_moth_p.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index dabda7bae8..5f46e90ec7 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -897,6 +897,8 @@ template struct InstrMeta { }; +QT_WARNING_PUSH +QT_WARNING_DISABLE_GCC("-Wuninitialized") #define MOTH_INSTR_META_TEMPLATE(I, FMT) \ template<> struct InstrMeta<(int)Instr::I> { \ enum { Size = MOTH_INSTR_SIZE(I, FMT) }; \ @@ -910,6 +912,7 @@ struct InstrMeta { }; FOR_EACH_MOTH_INSTR(MOTH_INSTR_META_TEMPLATE); #undef MOTH_INSTR_META_TEMPLATE +QT_WARNING_POP template class InstrData : public InstrMeta::DataType -- cgit v1.2.3 From a021bd87755ccfbe49e132f942ded935c9719b00 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Thu, 12 Jan 2017 18:02:45 +0100 Subject: Be (somewhat more) consistent about the value of pi Use M_PI in C++ and Math.PI in JavaScript (including QML). Use qmath.h's value for M_PI where we can't avoid an explicit value. Task-number: QTBUG-58083 Change-Id: Iabe938aff62ceac27b939ec33c6ee5e854aac15e Reviewed-by: Simon Hausmann --- src/quick/doc/snippets/qml/layerblending.qml | 2 +- src/quick/doc/snippets/qml/opacitymask.qml | 2 +- src/quick/util/qquicksvgparser.cpp | 8 +++----- 3 files changed, 5 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/quick/doc/snippets/qml/layerblending.qml b/src/quick/doc/snippets/qml/layerblending.qml index a922f7896c..3e75a607a6 100644 --- a/src/quick/doc/snippets/qml/layerblending.qml +++ b/src/quick/doc/snippets/qml/layerblending.qml @@ -62,7 +62,7 @@ Item { uniform highp vec2 pixelSize; varying highp vec2 qt_TexCoord0; void main() { - highp vec2 tc = sign(sin(3.14152 * qt_TexCoord0 * pixelSize)); + highp vec2 tc = sign(sin(3.14159265358979323846 * qt_TexCoord0 * pixelSize)); if (tc.x != tc.y) gl_FragColor = color1; else diff --git a/src/quick/doc/snippets/qml/opacitymask.qml b/src/quick/doc/snippets/qml/opacitymask.qml index beffb633d6..beb5cc6f67 100644 --- a/src/quick/doc/snippets/qml/opacitymask.qml +++ b/src/quick/doc/snippets/qml/opacitymask.qml @@ -63,7 +63,7 @@ Item { uniform highp vec2 pixelSize; varying highp vec2 qt_TexCoord0; void main() { - highp vec2 tc = sign(sin(3.14152 * qt_TexCoord0 * pixelSize)); + highp vec2 tc = sign(sin(3.14159265358979323846 * qt_TexCoord0 * pixelSize)); if (tc.x != tc.y) gl_FragColor = color1; else diff --git a/src/quick/util/qquicksvgparser.cpp b/src/quick/util/qquicksvgparser.cpp index 310b600965..086c6d0b28 100644 --- a/src/quick/util/qquicksvgparser.cpp +++ b/src/quick/util/qquicksvgparser.cpp @@ -45,8 +45,6 @@ QT_BEGIN_NAMESPACE -static const double Q_PI = 3.14159265358979323846; // pi - //copied from Qt SVG (qsvghandler.cpp). Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok); // '0' is 0x30 and '9' is 0x39 @@ -253,11 +251,11 @@ void QQuickSvgParser::pathArc(QPainterPath &path, th_arc = th1 - th0; if (th_arc < 0 && sweep_flag) - th_arc += 2 * Q_PI; + th_arc += 2 * M_PI; else if (th_arc > 0 && !sweep_flag) - th_arc -= 2 * Q_PI; + th_arc -= 2 * M_PI; - n_segs = qCeil(qAbs(th_arc / (Q_PI * 0.5 + 0.001))); + n_segs = qCeil(qAbs(th_arc / (M_PI * 0.5 + 0.001))); for (i = 0; i < n_segs; i++) { pathArcSegment(path, xc, yc, -- cgit v1.2.3 From 99651acdf7f34ad976522f12eb89ea8fc19ee0a5 Mon Sep 17 00:00:00 2001 From: Liang Qi Date: Tue, 13 Jun 2017 09:23:09 +0200 Subject: qmltest: fix compare() for urls url is object, but without any property. Task-number: QTBUG-61297 Change-Id: I68b0523be54e4d42f57267205ba8d66ff4ac4e30 Reviewed-by: Edward Welbourne --- src/imports/testlib/TestCase.qml | 4 ++++ src/qmltest/quicktestresult.cpp | 6 ++++++ 2 files changed, 10 insertions(+) (limited to 'src') diff --git a/src/imports/testlib/TestCase.qml b/src/imports/testlib/TestCase.qml index 18c70e1169..82211b5192 100644 --- a/src/imports/testlib/TestCase.qml +++ b/src/imports/testlib/TestCase.qml @@ -753,6 +753,10 @@ Item { bProperties.push(i); // collect exp's properties } + if (aProperties.length == 0 && bProperties.length == 0) { // at least a special case for QUrl + return eq && (JSON.stringify(act) == JSON.stringify(exp)); + } + // Ensures identical properties name return eq && qtest_compareInternal(aProperties.sort(), bProperties.sort()); diff --git a/src/qmltest/quicktestresult.cpp b/src/qmltest/quicktestresult.cpp index dc6caf505b..c4fb2b0f5f 100644 --- a/src/qmltest/quicktestresult.cpp +++ b/src/qmltest/quicktestresult.cpp @@ -512,6 +512,12 @@ void QuickTestResult::stringify(QQmlV4Function *args) result = QString::fromLatin1("Qt.vector3d(%1, %2, %3)").arg(v3d.x()).arg(v3d.y()).arg(v3d.z()); break; } + case QVariant::Url: + { + QUrl url = v.value(); + result = QString::fromLatin1("Qt.url(%1)").arg(url.toString()); + break; + } default: result = v.toString(); } -- cgit v1.2.3 From d2bb2e202427ebae545a408f065c893f3d837061 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 13 Jun 2017 09:42:54 +0200 Subject: Prospective build fix for qml compiler on Windows Do not try to decorate the internal MoveMapping class with an export macro that becomes an import macro on usage, when using it in the context of QtQmlDevTools. Change-Id: Id5349763572a144ac7d51d0af1f07d6cc19d683d Reviewed-by: Lars Knoll --- src/qml/compiler/qv4ssa_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h index 24257e99e9..b9d8ae0a6c 100644 --- a/src/qml/compiler/qv4ssa_p.h +++ b/src/qml/compiler/qv4ssa_p.h @@ -265,7 +265,7 @@ private: QHash startEndLoops; }; -class Q_AUTOTEST_EXPORT MoveMapping +class Q_QML_AUTOTEST_EXPORT MoveMapping { #ifdef V4_AUTOTEST public: -- cgit v1.2.3 From 158ba2d958a5099a381661abf775db4ff3ce75e9 Mon Sep 17 00:00:00 2001 From: Sami Nurmenniemi Date: Wed, 14 Jun 2017 14:44:52 +0300 Subject: Move default renderer selection from build to run time Previously software renderer was used as default only if OpenGL support was not compiled in. That does not work for example when using offscreen or minimal platforms with Qt that has OpenGL support compiled in. [ChangeLog][QtQuick] Selecting software as default renderer moved from build time to run time Task-number: QTBUG-60268 Change-Id: I4d1de09d8402af9c2b2feb36f8cfa92acd160243 Reviewed-by: Simon Hausmann Reviewed-by: Laszlo Agocs --- src/quick/scenegraph/qsgcontextplugin.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/quick/scenegraph/qsgcontextplugin.cpp b/src/quick/scenegraph/qsgcontextplugin.cpp index b8b5141957..bd311d3b46 100644 --- a/src/quick/scenegraph/qsgcontextplugin.cpp +++ b/src/quick/scenegraph/qsgcontextplugin.cpp @@ -49,6 +49,9 @@ #include #endif +#include +#include + QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_INFO) @@ -128,12 +131,12 @@ QSGAdaptationBackendData *contextFactory() if (requestedBackend.isEmpty() && qEnvironmentVariableIsSet("QT_QUICK_BACKEND")) requestedBackend = QString::fromLocal8Bit(qgetenv("QT_QUICK_BACKEND")); -#if !QT_CONFIG(opengl) - // If this is a build without OpenGL, and no backend has been set + // If this platform does not support OpenGL, and no backend has been set // default to the software renderer - if (requestedBackend.isEmpty()) + if (requestedBackend.isEmpty() + && !QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL)) { requestedBackend = QString::fromLocal8Bit("software"); -#endif + } if (!requestedBackend.isEmpty()) { qCDebug(QSG_LOG_INFO) << "Loading backend" << requestedBackend; -- cgit v1.2.3 From 6f2bdb816dc816461cf3888eb732f71904b6a7ab Mon Sep 17 00:00:00 2001 From: Jesus Fernandez Date: Tue, 30 May 2017 18:51:03 +0200 Subject: Add check in buffer creation Checks if the buffer was created successfully in QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache. In case of error shows a warning. Change-Id: I14565c411bbe1081f87b809ea417c44f8cefd9c5 Reviewed-by: Gunnar Sletta --- .../scenegraph/qsgdefaultdistancefieldglyphcache.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp index ba25172d2f..7789ef8fb1 100644 --- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp +++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp @@ -71,12 +71,15 @@ QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLCont , m_coreFuncs(0) #endif { - m_blitBuffer.create(); - m_blitBuffer.bind(); - static GLfloat buffer[16] = {-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, - 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f}; - m_blitBuffer.allocate(buffer, sizeof(buffer)); - m_blitBuffer.release(); + if (Q_LIKELY(m_blitBuffer.create())) { + m_blitBuffer.bind(); + static const GLfloat buffer[16] = {-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, + 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f}; + m_blitBuffer.allocate(buffer, sizeof(buffer)); + m_blitBuffer.release(); + } else { + qWarning("Buffer creation failed"); + } m_areaAllocator = new QSGAreaAllocator(QSize(maxTextureSize(), m_maxTextureCount * maxTextureSize())); } -- cgit v1.2.3 From df52c3e6a1489e68805f6ea025dc952ca4e21714 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 16 Jun 2017 11:32:51 +0200 Subject: Rename to vendorExtensionsEnable Fits existing Quick item naming patterns better. Change-Id: Id6d8f3653b33b1c1963bda4a2bcc212761e74caa Reviewed-by: Mitch Curtis --- src/imports/shapes/qquickshape.cpp | 10 +++++----- src/imports/shapes/qquickshape_p.h | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/imports/shapes/qquickshape.cpp b/src/imports/shapes/qquickshape.cpp index 0d060242b4..4e6896621e 100644 --- a/src/imports/shapes/qquickshape.cpp +++ b/src/imports/shapes/qquickshape.cpp @@ -623,7 +623,7 @@ void QQuickShapePath::resetFillGradient() \c{GL_NV_path_rendering} methods are available. The choice is made at runtime, depending on the graphics driver's capabilities. When this is not desired, applications can force using the generic method by setting the - Shape.enableVendorExtensions property to \c false. + Shape.vendorExtensionsEnabled property to \c false. \li The \c software backend is fully supported. The path is rendered via QPainter::strokePath() and QPainter::fillPath() in this case. @@ -745,7 +745,7 @@ void QQuickShape::setAsynchronous(bool async) } /*! - \qmlproperty bool QtQuick.Shapes::Shape::enableVendorExtensions + \qmlproperty bool QtQuick.Shapes::Shape::vendorExtensionsEnabled This property controls the usage of non-standard OpenGL extensions like GL_NV_path_rendering. To disable Shape.NvprRenderer and force a uniform @@ -755,18 +755,18 @@ void QQuickShape::setAsynchronous(bool async) The default value is \c true. */ -bool QQuickShape::enableVendorExtensions() const +bool QQuickShape::vendorExtensionsEnabled() const { Q_D(const QQuickShape); return d->enableVendorExts; } -void QQuickShape::setEnableVendorExtensions(bool enable) +void QQuickShape::setVendorExtensionsEnabled(bool enable) { Q_D(QQuickShape); if (d->enableVendorExts != enable) { d->enableVendorExts = enable; - emit enableVendorExtensionsChanged(); + emit vendorExtensionsEnabledChanged(); } } diff --git a/src/imports/shapes/qquickshape_p.h b/src/imports/shapes/qquickshape_p.h index c5096be229..db0b449c6c 100644 --- a/src/imports/shapes/qquickshape_p.h +++ b/src/imports/shapes/qquickshape_p.h @@ -225,7 +225,7 @@ class QQuickShape : public QQuickItem Q_OBJECT Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged) Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) - Q_PROPERTY(bool enableVendorExtensions READ enableVendorExtensions WRITE setEnableVendorExtensions NOTIFY enableVendorExtensionsChanged) + Q_PROPERTY(bool vendorExtensionsEnabled READ vendorExtensionsEnabled WRITE setVendorExtensionsEnabled NOTIFY vendorExtensionsEnabledChanged) Q_PROPERTY(Status status READ status NOTIFY statusChanged) Q_PROPERTY(QQmlListProperty data READ data) Q_CLASSINFO("DefaultProperty", "data") @@ -254,8 +254,8 @@ public: bool asynchronous() const; void setAsynchronous(bool async); - bool enableVendorExtensions() const; - void setEnableVendorExtensions(bool enable); + bool vendorExtensionsEnabled() const; + void setVendorExtensionsEnabled(bool enable); Status status() const; @@ -271,7 +271,7 @@ protected: Q_SIGNALS: void rendererChanged(); void asynchronousChanged(); - void enableVendorExtensionsChanged(); + void vendorExtensionsEnabledChanged(); void statusChanged(); private: -- cgit v1.2.3 From ee076afedccbe1d37306a7972051f84eb036d655 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 16 Jun 2017 11:36:39 +0200 Subject: Path and PathMove doc fixes Change-Id: Ife5f290d6860d2e0a41b61da1f7cc2bc995a4079 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/quick/util/qquickpath.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp index 0103f66814..15defdc01b 100644 --- a/src/quick/util/qquickpath.cpp +++ b/src/quick/util/qquickpath.cpp @@ -124,7 +124,7 @@ QT_BEGIN_NAMESPACE \li PathSvg \li Yes \li Yes - \li Yes, with limitations + \li Yes \li Yes \row \li PathAttribute @@ -1124,13 +1124,6 @@ void QQuickPathLine::addToPath(QPainterPath &path, const QQuickPathData &data) \ingroup qtquick-animation-paths \brief Moves the Path's position - While not relevant with PathView, for Path elements used with Shape it - is important to distinguish between the operations of drawing a straight - line and moving the path position without drawing anything. - - \note PathMove should not be used in a Path associated with a PathView. Use - PathLine instead. - The example below creates a path consisting of two horizontal lines with some empty space between them. All three segments have a width of 100: @@ -1143,6 +1136,11 @@ void QQuickPathLine::addToPath(QPainterPath &path, const QQuickPathData &data) } \endqml + \note PathMove should not be used in a Path associated with a PathView. Use + PathLine instead. For ShapePath however it is important to distinguish + between the operations of drawing a straight line and moving the path + position without drawing anything. + \sa Path, PathQuad, PathCubic, PathArc, PathCurve, PathSvg, PathLine */ @@ -1930,10 +1928,10 @@ 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 Shape 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. + \note Mixing PathSvg with other type of elements is not always supported. + For example, when \l Shape is backed by \c{GL_NV_path_rendering}, a + ShapePath 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 */ -- cgit v1.2.3 From f2e44812a739b9f481213c67e7683b99676f2593 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 15 Jun 2017 11:38:35 +0200 Subject: Prospective build fix for Integrity OS If ExecutionEngine is forward declared and the compiler tries to instantiate the Value/Array/etc. templates early on, it may not be able to map ExecutionEngine to EngineBase. That is what the error message suggests. Since we don't need ExecutionEngine let's try EngineBase. Change-Id: Idd18dd431705cce8df79180e7ac08574bbe1170c Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4arraydata_p.h | 12 ++++++------ src/qml/jsruntime/qv4memberdata_p.h | 4 ++-- src/qml/memory/qv4writebarrier_p.h | 14 +++++++------- 3 files changed, 15 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/qml/jsruntime/qv4arraydata_p.h b/src/qml/jsruntime/qv4arraydata_p.h index 1d895c90c5..e1de2e82e6 100644 --- a/src/qml/jsruntime/qv4arraydata_p.h +++ b/src/qml/jsruntime/qv4arraydata_p.h @@ -107,7 +107,7 @@ DECLARE_HEAP_OBJECT(ArrayData, Base) { Heap::ArrayData *arrayData; uint index; - void set(ExecutionEngine *e, Value newVal) { + void set(EngineBase *e, Value newVal) { arrayData->values.set(e, index, newVal); } const Value *operator->() const { return &arrayData->values[index]; } @@ -123,7 +123,7 @@ DECLARE_HEAP_OBJECT(ArrayData, Base) { return vtable()->get(this, i); } inline bool getProperty(uint index, Property *p, PropertyAttributes *attrs); - inline void setProperty(ExecutionEngine *e, uint index, const Property *p); + inline void setProperty(EngineBase *e, uint index, const Property *p); inline Index getValueOrSetter(uint index, PropertyAttributes *attrs); inline PropertyAttributes attributes(uint i) const; @@ -135,7 +135,7 @@ DECLARE_HEAP_OBJECT(ArrayData, Base) { return vtable()->length(this); } - void setArrayData(ExecutionEngine *e, uint index, Value newVal) { + void setArrayData(EngineBase *e, uint index, Value newVal) { values.set(e, index, newVal); } @@ -146,7 +146,7 @@ V4_ASSERT_IS_TRIVIAL(ArrayData) struct SimpleArrayData : public ArrayData { uint mappedIndex(uint index) const { return (index + offset) % values.alloc; } const Value &data(uint index) const { return values[mappedIndex(index)]; } - void setData(ExecutionEngine *e, uint index, Value newVal) { + void setData(EngineBase *e, uint index, Value newVal) { values.set(e, mappedIndex(index), newVal); } @@ -197,7 +197,7 @@ struct Q_QML_EXPORT ArrayData : public Managed PropertyAttributes *attrs() const { return d()->attrs; } void setAttrs(PropertyAttributes *a) { d()->attrs = a; } const Value *arrayData() const { return d()->values.data(); } - void setArrayData(ExecutionEngine *e, uint index, Value newVal) { + void setArrayData(EngineBase *e, uint index, Value newVal) { d()->setArrayData(e, index, newVal); } @@ -310,7 +310,7 @@ bool ArrayData::getProperty(uint index, Property *p, PropertyAttributes *attrs) return true; } -void ArrayData::setProperty(QV4::ExecutionEngine *e, uint index, const Property *p) +void ArrayData::setProperty(QV4::EngineBase *e, uint index, const Property *p) { uint mapped = mappedIndex(index); Q_ASSERT(mapped != UINT_MAX); diff --git a/src/qml/jsruntime/qv4memberdata_p.h b/src/qml/jsruntime/qv4memberdata_p.h index 9d333011fc..bae524d088 100644 --- a/src/qml/jsruntime/qv4memberdata_p.h +++ b/src/qml/jsruntime/qv4memberdata_p.h @@ -88,8 +88,8 @@ struct MemberData : Managed const Value &operator[] (uint idx) const { return d()->values[idx]; } const Value *data() const { return d()->values.data(); } - void set(ExecutionEngine *e, uint index, Value v) { d()->values.set(e, index, v); } - void set(ExecutionEngine *e, uint index, Heap::Base *b) { d()->values.set(e, index, b); } + void set(EngineBase *e, uint index, Value v) { d()->values.set(e, index, v); } + void set(EngineBase *e, uint index, Heap::Base *b) { d()->values.set(e, index, b); } inline uint size() const { return d()->values.size; } diff --git a/src/qml/memory/qv4writebarrier_p.h b/src/qml/memory/qv4writebarrier_p.h index 3182fd2aa9..86fd28000d 100644 --- a/src/qml/memory/qv4writebarrier_p.h +++ b/src/qml/memory/qv4writebarrier_p.h @@ -123,7 +123,7 @@ struct Pointer { return base; } - void set(ExecutionEngine *e, T newVal) { + void set(EngineBase *e, T newVal) { WriteBarrier::write(e, base(), reinterpret_cast(&ptr), reinterpret_cast(newVal)); } @@ -146,10 +146,10 @@ struct HeapValue : Value { return base; } - void set(ExecutionEngine *e, const Value &newVal) { + void set(EngineBase *e, const Value &newVal) { WriteBarrier::write(e, base(), this, newVal); } - void set(ExecutionEngine *e, Heap::Base *b) { + void set(EngineBase *e, Heap::Base *b) { WriteBarrier::write(e, base(), this, b); } }; @@ -166,10 +166,10 @@ struct ValueArray { return base; } - void set(ExecutionEngine *e, uint index, Value v) { + void set(EngineBase *e, uint index, Value v) { WriteBarrier::write(e, base(), values + index, v); } - void set(ExecutionEngine *e, uint index, Heap::Base *b) { + void set(EngineBase *e, uint index, Heap::Base *b) { WriteBarrier::write(e, base(), values + index, b); } inline const Value &operator[] (uint index) const { @@ -180,13 +180,13 @@ struct ValueArray { return values; } - void insertData(ExecutionEngine *e, uint index, Value v) { + void insertData(EngineBase *e, uint index, Value v) { for (uint i = size - 1; i > index; --i) { values[i] = values[i - 1]; } set(e, index, v); } - void removeData(ExecutionEngine *e, uint index, int n = 1) { + void removeData(EngineBase *e, uint index, int n = 1) { Q_UNUSED(e); for (uint i = index; i < size - n; ++i) { values[i] = values[i + n]; -- cgit v1.2.3 From 87d9baa677edd7aee5c3c6544b4fc1770f7e5ac8 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Thu, 1 Jun 2017 14:52:23 +0200 Subject: QQuickImplicitSizeItem: don't redefine implicit size change signals f7ada9b9 redefined QQuickItem's implicitWidthChanged and implicitHeightChanged signals in order to avoid warnings. 2ca187ca makes it possible to reuse the base class' signals as long as they don't have parameters. This change should be transparent to users. [ChangeLog][QtQuick][Important Behavior Change] The implicitWidthChanged2 and implicitHeightChanged2 signals of QQuickImplicitSizeItem have been removed. Although these were undocumented and hence private API, any applications currently using them should use the public implicitWidthChanged and implicitHeightChanged signals. Task-number: QTCREATORBUG-15000 Change-Id: I35cfdefc6c992f4529b60c979265650c91ba8549 Reviewed-by: Albert Astals Cid Reviewed-by: J-P Nurmi Reviewed-by: Robin Burchell --- src/quick/items/qquickimplicitsizeitem.cpp | 26 ++++---------------------- src/quick/items/qquickimplicitsizeitem_p.h | 8 ++------ src/quick/items/qquickimplicitsizeitem_p_p.h | 3 --- 3 files changed, 6 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickimplicitsizeitem.cpp b/src/quick/items/qquickimplicitsizeitem.cpp index 1996fb9489..2569b2a224 100644 --- a/src/quick/items/qquickimplicitsizeitem.cpp +++ b/src/quick/items/qquickimplicitsizeitem.cpp @@ -45,29 +45,11 @@ QT_BEGIN_NAMESPACE /*! \internal - The purpose of QQuickImplicitSizeItem is not immediately clear, as both - the implicit size properties and signals exist on QQuickItem. However, - for some items - where the implicit size has an underlying meaning (such as - Image, where the implicit size represents the real size of the image) - having implicit size writable is an undesirable thing. - - QQuickImplicitSizeItem redefines the properties as being readonly. - Unfortunately, this also means they need to redefine the change signals. - See QTBUG-30258 for more information. + QQuickImplicitSizeItem redefines the implicitWidth and implicitHeight + properties as readonly, as some items (e.g. Image, where the implicit size + represents the real size of the image) should not be able to have their + implicit size modified. */ -void QQuickImplicitSizeItemPrivate::implicitWidthChanged() -{ - Q_Q(QQuickImplicitSizeItem); - QQuickItemPrivate::implicitWidthChanged(); - emit q->implicitWidthChanged2(); -} - -void QQuickImplicitSizeItemPrivate::implicitHeightChanged() -{ - Q_Q(QQuickImplicitSizeItem); - QQuickItemPrivate::implicitHeightChanged(); - emit q->implicitHeightChanged2(); -} QQuickImplicitSizeItem::QQuickImplicitSizeItem(QQuickImplicitSizeItemPrivate &dd, QQuickItem *parent) : QQuickItem(dd, parent) diff --git a/src/quick/items/qquickimplicitsizeitem_p.h b/src/quick/items/qquickimplicitsizeitem_p.h index 75b04449f8..8ae8f9f447 100644 --- a/src/quick/items/qquickimplicitsizeitem_p.h +++ b/src/quick/items/qquickimplicitsizeitem_p.h @@ -60,16 +60,12 @@ class QQuickImplicitSizeItemPrivate; class Q_QUICK_PRIVATE_EXPORT QQuickImplicitSizeItem : public QQuickItem { Q_OBJECT - Q_PROPERTY(qreal implicitWidth READ implicitWidth NOTIFY implicitWidthChanged2) - Q_PROPERTY(qreal implicitHeight READ implicitHeight NOTIFY implicitHeightChanged2) + Q_PROPERTY(qreal implicitWidth READ implicitWidth NOTIFY implicitWidthChanged) + Q_PROPERTY(qreal implicitHeight READ implicitHeight NOTIFY implicitHeightChanged) protected: QQuickImplicitSizeItem(QQuickImplicitSizeItemPrivate &dd, QQuickItem *parent); -Q_SIGNALS: - Q_REVISION(1) void implicitWidthChanged2(); - Q_REVISION(1) void implicitHeightChanged2(); - private: Q_DISABLE_COPY(QQuickImplicitSizeItem) Q_DECLARE_PRIVATE(QQuickImplicitSizeItem) diff --git a/src/quick/items/qquickimplicitsizeitem_p_p.h b/src/quick/items/qquickimplicitsizeitem_p_p.h index 2c42fa62f2..0495cf87e1 100644 --- a/src/quick/items/qquickimplicitsizeitem_p_p.h +++ b/src/quick/items/qquickimplicitsizeitem_p_p.h @@ -65,9 +65,6 @@ public: QQuickImplicitSizeItemPrivate() { } - - void implicitWidthChanged() Q_DECL_OVERRIDE; - void implicitHeightChanged() Q_DECL_OVERRIDE; }; QT_END_NAMESPACE -- cgit v1.2.3 From 463a1ed22a10044b5e719a84abc47c8270b70efd Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 21 Jun 2017 14:25:53 +0200 Subject: Fix lookup of formals in QML signal handlers with AOT Partially revert commit 38221427bc21a11b96de7fa7666264c34298c0c0 to allow for the lookup of formals by name even when using the simple call context. Task-number: QTBUG-61531 Change-Id: Ic5b235b62949ce050817ef2937bd4a35dd64aa6a Reviewed-by: Lars Knoll --- src/qml/jsruntime/qv4context.cpp | 63 ++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index ba72a9e43b..6807c835b0 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -237,16 +237,14 @@ bool ExecutionContext::deleteProperty(String *name) return global->deleteProperty(name); break; } - case Heap::ExecutionContext::Type_CallContext: { - Heap::CallContext *c = static_cast(ctx->d()); + case Heap::ExecutionContext::Type_CallContext: + Q_FALLTHROUGH(); + case Heap::ExecutionContext::Type_SimpleCallContext: { + Heap::SimpleCallContext *c = static_cast(ctx->d()); uint index = c->v4Function->internalClass->find(id); if (index < UINT_MAX) // ### throw in strict mode? return false; - Q_FALLTHROUGH(); - } - case Heap::ExecutionContext::Type_SimpleCallContext: { - Heap::SimpleCallContext *c = static_cast(ctx->d()); ScopedObject qml(scope, c->activation); if (qml && qml->hasProperty(name)) return qml->deleteProperty(name); @@ -418,26 +416,21 @@ ReturnedValue ExecutionContext::getProperty(String *name) return v->asReturnedValue(); break; } - case Heap::ExecutionContext::Type_CallContext: { - Heap::CallContext *c = static_cast(ctx->d()); + case Heap::ExecutionContext::Type_CallContext: + Q_FALLTHROUGH(); + case Heap::ExecutionContext::Type_SimpleCallContext: { + Heap::SimpleCallContext *c = static_cast(ctx->d()); + name->makeIdentifier(); Identifier *id = name->identifier(); - uint index = c->v4Function->internalClass->find(id); if (index < UINT_MAX) { if (index < c->v4Function->nFormals) return c->callData->args[c->v4Function->nFormals - index - 1].asReturnedValue(); - Q_ASSERT(c->type == Heap::ExecutionContext::Type_CallContext); - return c->locals[index - c->v4Function->nFormals].asReturnedValue(); + if (c->type == Heap::ExecutionContext::Type_CallContext) + return static_cast(c)->locals[index - c->v4Function->nFormals].asReturnedValue(); } - if (c->v4Function->isNamedExpression()) { - if (c->function && name->equals(ScopedString(scope, c->v4Function->name()))) - return c->function->asReturnedValue(); - } - Q_FALLTHROUGH(); - } - case Heap::ExecutionContext::Type_SimpleCallContext: { - Heap::SimpleCallContext *c = static_cast(ctx->d()); + ScopedObject activation(scope, c->activation); if (activation) { bool hasProperty = false; @@ -445,6 +438,12 @@ ReturnedValue ExecutionContext::getProperty(String *name) if (hasProperty) return v->asReturnedValue(); } + + if (c->v4Function->isNamedExpression() && c->type == Heap::ExecutionContext::Type_CallContext) { + if (name->equals(ScopedString(scope, c->v4Function->name()))) + if (auto func = static_cast(c)->function) + return func->asReturnedValue(); + } break; } case Heap::ExecutionContext::Type_QmlContext: { @@ -498,25 +497,21 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) return v->asReturnedValue(); break; } - case Heap::ExecutionContext::Type_CallContext: { - Heap::CallContext *c = static_cast(ctx->d()); + case Heap::ExecutionContext::Type_CallContext: + Q_FALLTHROUGH(); + case Heap::ExecutionContext::Type_SimpleCallContext: { + Heap::SimpleCallContext *c = static_cast(ctx->d()); + name->makeIdentifier(); Identifier *id = name->identifier(); - uint index = c->v4Function->internalClass->find(id); if (index < UINT_MAX) { if (index < c->v4Function->nFormals) return c->callData->args[c->v4Function->nFormals - index - 1].asReturnedValue(); - return c->locals[index - c->v4Function->nFormals].asReturnedValue(); - } - if (c->v4Function->isNamedExpression()) { - if (c->function && name->equals(ScopedString(scope, c->v4Function->name()))) - return c->function->asReturnedValue(); + if (c->type == Heap::ExecutionContext::Type_CallContext) + return static_cast(c)->locals[index - c->v4Function->nFormals].asReturnedValue(); } - Q_FALLTHROUGH(); - } - case Heap::ExecutionContext::Type_SimpleCallContext: { - Heap::SimpleCallContext *c = static_cast(ctx->d()); + ScopedObject activation(scope, c->activation); if (activation) { bool hasProperty = false; @@ -524,6 +519,12 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) if (hasProperty) return v->asReturnedValue(); } + + if (c->v4Function->isNamedExpression() && c->type == Heap::ExecutionContext::Type_CallContext) { + if (name->equals(ScopedString(scope, c->v4Function->name()))) + if (auto func = static_cast(c)->function) + return func->asReturnedValue(); + } break; } case Heap::ExecutionContext::Type_QmlContext: { -- cgit v1.2.3 From aa048e7a6c4c1b72839cea95c386c6ccc12be420 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Fri, 2 Jun 2017 08:01:18 +0200 Subject: Update plugins.qmltypes Task-number: QTCREATORBUG-15000 Change-Id: Ia184b442d2adff9a92878f80355b01e79114132b Reviewed-by: J-P Nurmi --- src/imports/qtquick2/plugins.qmltypes | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/imports/qtquick2/plugins.qmltypes b/src/imports/qtquick2/plugins.qmltypes index 834b4bfac2..73d6d8ec68 100644 --- a/src/imports/qtquick2/plugins.qmltypes +++ b/src/imports/qtquick2/plugins.qmltypes @@ -4,7 +4,7 @@ import QtQuick.tooling 1.2 // It is used for QML tooling purposes only. // // This file was auto-generated by: -// 'qmlplugindump -nonrelocatable -noforceqtquick QtQuick 2.9' +// 'qmlplugindump -nonrelocatable -noforceqtquick QtQuick 2.10' Module { dependencies: [] @@ -1517,8 +1517,12 @@ Module { name: "QQuickFlickable" defaultProperty: "flickableData" prototype: "QQuickItem" - exports: ["QtQuick/Flickable 2.0", "QtQuick/Flickable 2.9"] - exportMetaObjectRevisions: [0, 9] + exports: [ + "QtQuick/Flickable 2.0", + "QtQuick/Flickable 2.10", + "QtQuick/Flickable 2.9" + ] + exportMetaObjectRevisions: [0, 10, 9] Enum { name: "BoundsBehavior" values: { @@ -1528,6 +1532,12 @@ Module { "DragAndOvershootBounds": 3 } } + Enum { + name: "BoundsMovement" + values: { + "FollowBoundsBehavior": 1 + } + } Enum { name: "FlickableDirection" values: { @@ -1552,6 +1562,7 @@ Module { Property { name: "horizontalVelocity"; type: "double"; isReadonly: true } Property { name: "verticalVelocity"; type: "double"; isReadonly: true } Property { name: "boundsBehavior"; type: "BoundsBehavior" } + Property { name: "boundsMovement"; revision: 10; type: "BoundsMovement" } Property { name: "rebound"; type: "QQuickTransition"; isPointer: true } Property { name: "maximumFlickVelocity"; type: "double" } Property { name: "flickDeceleration"; type: "double" } @@ -1583,6 +1594,7 @@ Module { Property { name: "flickableData"; type: "QObject"; isList: true; isReadonly: true } Property { name: "flickableChildren"; type: "QQuickItem"; isList: true; isReadonly: true } Signal { name: "isAtBoundaryChanged" } + Signal { name: "boundsMovementChanged"; revision: 10 } Signal { name: "movementStarted" } Signal { name: "movementEnded" } Signal { name: "flickStarted" } @@ -1811,7 +1823,6 @@ Module { exports: ["QtQuick/Gradient 2.0"] exportMetaObjectRevisions: [0] Property { name: "stops"; type: "QQuickGradientStop"; isList: true; isReadonly: true } - Signal { name: "updated" } } Component { name: "QQuickGradientStop" @@ -2088,8 +2099,6 @@ Module { prototype: "QQuickItem" Property { name: "implicitWidth"; type: "double"; isReadonly: true } Property { name: "implicitHeight"; type: "double"; isReadonly: true } - Signal { name: "implicitWidthChanged2"; revision: 1 } - Signal { name: "implicitHeightChanged2"; revision: 1 } } Component { name: "QQuickIntValidator" @@ -2300,6 +2309,7 @@ Module { Property { name: "samplerName"; type: "QByteArray" } Property { name: "effect"; type: "QQmlComponent"; isPointer: true } Property { name: "textureMirroring"; type: "QQuickShaderEffectSource::TextureMirroring" } + Property { name: "samples"; type: "int" } Signal { name: "enabledChanged" Parameter { name: "enabled"; type: "bool" } @@ -2340,6 +2350,10 @@ Module { name: "textureMirroringChanged" Parameter { name: "mirroring"; type: "QQuickShaderEffectSource::TextureMirroring" } } + Signal { + name: "samplesChanged" + Parameter { name: "count"; type: "int" } + } } Component { name: "QQuickItemView" @@ -3594,9 +3608,10 @@ Module { prototype: "QQuickItem" exports: [ "QtQuick/ShaderEffectSource 2.0", - "QtQuick/ShaderEffectSource 2.6" + "QtQuick/ShaderEffectSource 2.6", + "QtQuick/ShaderEffectSource 2.9" ] - exportMetaObjectRevisions: [0, 1] + exportMetaObjectRevisions: [0, 1, 2] Enum { name: "WrapMode" values: { @@ -3632,6 +3647,7 @@ Module { Property { name: "mipmap"; type: "bool" } Property { name: "recursive"; type: "bool" } Property { name: "textureMirroring"; revision: 1; type: "TextureMirroring" } + Property { name: "samples"; revision: 2; type: "int" } Signal { name: "scheduledUpdateCompleted" } Method { name: "scheduleUpdate" } } @@ -4084,6 +4100,7 @@ Module { Property { name: "rightPadding"; revision: 6; type: "double" } Property { name: "bottomPadding"; revision: 6; type: "double" } Property { name: "fontInfo"; revision: 9; type: "QJSValue"; isReadonly: true } + Property { name: "advance"; revision: 10; type: "QSizeF"; isReadonly: true } Signal { name: "textChanged" Parameter { name: "text"; type: "string" } -- cgit v1.2.3 From 717618d0b05b751ab155c55a6c2b03c56cffc99b Mon Sep 17 00:00:00 2001 From: Yevhen Kyriukha Date: Sun, 21 May 2017 23:02:37 +0300 Subject: Support external textures (samplerExternalOES) in ShaderEffect samplerExternalOES type defined in OES_EGL_image_external extension is used on embedded platforms to achieve zero-copy of textures. [ChangeLog][Qt Quick] Added support for samplerExternalOES sampler type in ShaderEffect Task-number: QTBUG-59462 Change-Id: Ib1e864f2e1321949b0a6539d37b92d2b62541ea8 Reviewed-by: Laszlo Agocs --- src/quick/items/qquickopenglshadereffect.cpp | 28 ++++++++++++++---------- src/quick/items/qquickopenglshadereffectnode.cpp | 15 +++++++++---- src/quick/items/qquickopenglshadereffectnode_p.h | 2 +- 3 files changed, 29 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickopenglshadereffect.cpp b/src/quick/items/qquickopenglshadereffect.cpp index 4fcfe04b55..c3e0ba05bd 100644 --- a/src/quick/items/qquickopenglshadereffect.cpp +++ b/src/quick/items/qquickopenglshadereffect.cpp @@ -230,7 +230,7 @@ void QQuickOpenGLShaderEffectCommon::disconnectPropertySignals(QQuickItem *item, auto mapper = signalMappers[shaderType].at(i); void *a = mapper; QObjectPrivate::disconnect(item, mapper->signalIndex(), &a); - if (d.specialType == UniformData::Sampler) { + if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) { QQuickItem *source = qobject_cast(qvariant_cast(d.value)); if (source) { if (item->window()) @@ -272,7 +272,7 @@ void QQuickOpenGLShaderEffectCommon::connectPropertySignals(QQuickItem *item, qWarning("QQuickOpenGLShaderEffect: '%s' does not have a matching property!", d.name.constData()); } - if (d.specialType == UniformData::Sampler) { + if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) { QQuickItem *source = qobject_cast(qvariant_cast(d.value)); if (source) { if (item->window()) @@ -335,6 +335,7 @@ void QQuickOpenGLShaderEffectCommon::lookThroughShaderCode(QQuickItem *item, Q_ASSERT(decl == UniformQualifier); const int sampLen = sizeof("sampler2D") - 1; + const int sampExtLen = sizeof("samplerExternalOES") - 1; const int opLen = sizeof("qt_Opacity") - 1; const int matLen = sizeof("qt_Matrix") - 1; const int srLen = sizeof("qt_SubRect_") - 1; @@ -357,8 +358,12 @@ void QQuickOpenGLShaderEffectCommon::lookThroughShaderCode(QQuickItem *item, mapper = new QtPrivate::MappedSlotObject([this, mappedId](){ this->mappedPropertyChanged(mappedId); }); - bool sampler = typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0; - d.specialType = sampler ? UniformData::Sampler : UniformData::None; + if (typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0) + d.specialType = UniformData::Sampler; + else if (typeLength == sampExtLen && qstrncmp("samplerExternalOES", s + typeIndex, sampExtLen) == 0) + d.specialType = UniformData::SamplerExternal; + else + d.specialType = UniformData::None; d.setValueFromProperty(item, itemMetaObject); } uniformData[shaderType].append(d); @@ -451,7 +456,8 @@ void QQuickOpenGLShaderEffectCommon::updateMaterial(QQuickOpenGLShaderEffectNode int textureProviderCount = 0; for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { for (int i = 0; i < uniformData[shaderType].size(); ++i) { - if (uniformData[shaderType].at(i).specialType == UniformData::Sampler) + if (uniformData[shaderType].at(i).specialType == UniformData::Sampler || + uniformData[shaderType].at(i).specialType == UniformData::SamplerExternal) ++textureProviderCount; } material->uniforms[shaderType] = uniformData[shaderType]; @@ -474,7 +480,7 @@ void QQuickOpenGLShaderEffectCommon::updateMaterial(QQuickOpenGLShaderEffectNode for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { for (int i = 0; i < uniformData[shaderType].size(); ++i) { const UniformData &d = uniformData[shaderType].at(i); - if (d.specialType != UniformData::Sampler) + if (d.specialType != UniformData::Sampler && d.specialType != UniformData::SamplerExternal) continue; QSGTextureProvider *oldProvider = material->textureProviders.at(index); QSGTextureProvider *newProvider = 0; @@ -513,7 +519,7 @@ void QQuickOpenGLShaderEffectCommon::updateWindow(QQuickWindow *window) for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { for (int i = 0; i < uniformData[shaderType].size(); ++i) { const UniformData &d = uniformData[shaderType].at(i); - if (d.specialType == UniformData::Sampler) { + if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) { QQuickItem *source = qobject_cast(qvariant_cast(d.value)); if (source) QQuickItemPrivate::get(source)->refWindow(window); @@ -524,7 +530,7 @@ void QQuickOpenGLShaderEffectCommon::updateWindow(QQuickWindow *window) for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { for (int i = 0; i < uniformData[shaderType].size(); ++i) { const UniformData &d = uniformData[shaderType].at(i); - if (d.specialType == UniformData::Sampler) { + if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) { QQuickItem *source = qobject_cast(qvariant_cast(d.value)); if (source) QQuickItemPrivate::get(source)->derefWindow(); @@ -539,7 +545,7 @@ void QQuickOpenGLShaderEffectCommon::sourceDestroyed(QObject *object) for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { for (int i = 0; i < uniformData[shaderType].size(); ++i) { UniformData &d = uniformData[shaderType][i]; - if (d.specialType == UniformData::Sampler && d.value.canConvert()) { + if ((d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) && d.value.canConvert()) { if (qvariant_cast(d.value) == object) d.value = QVariant(); } @@ -554,7 +560,7 @@ static bool qquick_uniqueInUniformData(QQuickItem *source, const QVector(d.value) == source) + if ((d.specialType == QQuickOpenGLShaderEffectMaterial::UniformData::Sampler || d.specialType == QQuickOpenGLShaderEffectMaterial::UniformData::SamplerExternal) && qvariant_cast(d.value) == source) return false; } } @@ -568,7 +574,7 @@ void QQuickOpenGLShaderEffectCommon::propertyChanged(QQuickItem *item, Key::ShaderType shaderType = Key::ShaderType(mappedId >> 16); int index = mappedId & 0xffff; UniformData &d = uniformData[shaderType][index]; - if (d.specialType == UniformData::Sampler) { + if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) { QQuickItem *source = qobject_cast(qvariant_cast(d.value)); if (source) { if (item->window()) diff --git a/src/quick/items/qquickopenglshadereffectnode.cpp b/src/quick/items/qquickopenglshadereffectnode.cpp index e1ea98641d..5dbfee73cb 100644 --- a/src/quick/items/qquickopenglshadereffectnode.cpp +++ b/src/quick/items/qquickopenglshadereffectnode.cpp @@ -47,6 +47,10 @@ #include #include +#ifndef GL_TEXTURE_EXTERNAL_OES +#define GL_TEXTURE_EXTERNAL_OES 0x8D65 +#endif + QT_BEGIN_NAMESPACE static bool hasAtlasTexture(const QVector &textureProviders) @@ -124,7 +128,7 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri for (int i = 0; i < material->uniforms[shaderType].size(); ++i) { const UniformData &d = material->uniforms[shaderType].at(i); QByteArray name = d.name; - if (d.specialType == UniformData::Sampler) { + if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) { program()->setUniformValue(d.name.constData(), textureProviderIndex++); // We don't need to store the sampler uniform locations, since their values // only need to be set once. Look for the "qt_SubRect_" uniforms instead. @@ -143,7 +147,7 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri for (int i = 0; i < material->uniforms[shaderType].size(); ++i) { const UniformData &d = material->uniforms[shaderType].at(i); int loc = m_uniformLocs[shaderType].at(i); - if (d.specialType == UniformData::Sampler) { + if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) { int idx = textureProviderIndex++; functions->glActiveTexture(GL_TEXTURE0 + idx); if (QSGTextureProvider *provider = material->textureProviders.at(idx)) { @@ -164,7 +168,10 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri continue; } } - functions->glBindTexture(GL_TEXTURE_2D, 0); + if (d.specialType == UniformData::Sampler) + functions->glBindTexture(GL_TEXTURE_2D, 0); + else if (d.specialType == UniformData::SamplerExternal) + functions->glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0); } else if (d.specialType == UniformData::Opacity) { program()->setUniformValue(loc, state.opacity()); } else if (d.specialType == UniformData::Matrix) { @@ -396,7 +403,7 @@ bool QQuickOpenGLShaderEffectMaterial::UniformData::operator == (const UniformDa if (name != other.name) return false; - if (specialType == UniformData::Sampler) { + if (specialType == UniformData::Sampler || specialType == UniformData::SamplerExternal) { // We can't check the source objects as these live in the GUI thread, // so return true here and rely on the textureProvider check for // equality of these.. diff --git a/src/quick/items/qquickopenglshadereffectnode_p.h b/src/quick/items/qquickopenglshadereffectnode_p.h index aea28e6612..784294d9eb 100644 --- a/src/quick/items/qquickopenglshadereffectnode_p.h +++ b/src/quick/items/qquickopenglshadereffectnode_p.h @@ -90,7 +90,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffectMaterial : public QSGMateri public: struct UniformData { - enum SpecialType { None, Sampler, SubRect, Opacity, Matrix }; + enum SpecialType { None, Sampler, SamplerExternal, SubRect, Opacity, Matrix }; QByteArray name; QVariant value; -- cgit v1.2.3 From e3cf025bc041dab43f0432b15adb9f46b4ec8991 Mon Sep 17 00:00:00 2001 From: Jesus Fernandez Date: Sun, 25 Jun 2017 11:29:45 +0200 Subject: Initialize pointer *** CID 181282: Uninitialized members (UNINIT_CTOR) /qtdeclarative/src/imports/shapes/qquickshapegenericrenderer_p.h: 85 in QQuickShapeGenericRenderer::QQuickShapeGenericRenderer(QQuickItem *)() 79 QQuickShapeGenericRenderer(QQuickItem *item) 80 : m_item(item), 81 m_api(QSGRendererInterface::Unknown), 82 m_rootNode(nullptr), 83 m_accDirty(0), 84 m_asyncCallback(nullptr) >>> CID 181282: Uninitialized members (UNINIT_CTOR) >>> Non-static class member "m_asyncCallbackData" is not initialized in this constructor nor in any functions that it calls. 85 { } 86 ~QQuickShapeGenericRenderer(); 87 88 void beginSync(int totalCount) override; 89 void setPath(int index, const QQuickPath *path) override; 90 void setJSPath(int index, const QQuickShapePathCommands &path) override; Change-Id: Ifce4e20c8372b392900b77f45ae14d2abfa1657d Coverity-Id: 181282 Reviewed-by: Laszlo Agocs --- src/imports/shapes/qquickshapegenericrenderer_p.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/imports/shapes/qquickshapegenericrenderer_p.h b/src/imports/shapes/qquickshapegenericrenderer_p.h index ba50f50309..430a95465b 100644 --- a/src/imports/shapes/qquickshapegenericrenderer_p.h +++ b/src/imports/shapes/qquickshapegenericrenderer_p.h @@ -81,7 +81,8 @@ public: m_api(QSGRendererInterface::Unknown), m_rootNode(nullptr), m_accDirty(0), - m_asyncCallback(nullptr) + m_asyncCallback(nullptr), + m_asyncCallbackData(nullptr) { } ~QQuickShapeGenericRenderer(); -- cgit v1.2.3 From 47b59f9001ea6c185e44f243a1fb697b1859215b Mon Sep 17 00:00:00 2001 From: Jesus Fernandez Date: Sun, 25 Jun 2017 11:27:21 +0200 Subject: Initialize pointer *** CID 181281: Uninitialized members (UNINIT_CTOR) /qtdeclarative/src/imports/shapes/qquickshapenvprrenderer_p.h: 156 in QQuickNvprMaterialManager::QQuickNvprMaterialManager()() 150 151 void create(QQuickNvprFunctions *nvpr); 152 MaterialDesc *activateMaterial(Material m); 153 void releaseResources(); 154 155 private: >>> CID 181281: Uninitialized members (UNINIT_CTOR) >>> The compiler-generated constructor for this class does not initialize "m_nvpr". 156 QQuickNvprFunctions *m_nvpr; 157 MaterialDesc m_materials[NMaterials]; 158 }; 159 160 class QQuickNvprBlitter 161 { Change-Id: Ia502683b55022ac3eafd4ebd015d44273185bdfc Coverity-Id: 181281 Reviewed-by: Laszlo Agocs --- src/imports/shapes/qquickshapenvprrenderer_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/imports/shapes/qquickshapenvprrenderer_p.h b/src/imports/shapes/qquickshapenvprrenderer_p.h index 33007313d4..d90357a40d 100644 --- a/src/imports/shapes/qquickshapenvprrenderer_p.h +++ b/src/imports/shapes/qquickshapenvprrenderer_p.h @@ -153,7 +153,7 @@ public: void releaseResources(); private: - QQuickNvprFunctions *m_nvpr; + QQuickNvprFunctions *m_nvpr = nullptr; MaterialDesc m_materials[NMaterials]; }; -- cgit v1.2.3 From 1fa071b2b70ee6314fa12312da2d930f3983a7cc Mon Sep 17 00:00:00 2001 From: Jesus Fernandez Date: Sun, 25 Jun 2017 11:21:33 +0200 Subject: Initialize variables *** CID 181280: Uninitialized members (UNINIT_CTOR) /qtdeclarative/src/imports/shapes/qquickshapegenericrenderer.cpp: 700 in QQuickShapeLinearGradientShader::QQuickShapeLinearGradientShader()() 694 QQuickShapeLinearGradientShader::QQuickShapeLinearGradientShader() 695 { 696 setShaderSourceFile(QOpenGLShader::Vertex, 697 QStringLiteral(":/qt-project.org/items/shaders/lineargradient.vert")); 698 setShaderSourceFile(QOpenGLShader::Fragment, 699 QStringLiteral(":/qt-project.org/items/shaders/lineargradient.frag")); >>> CID 181280: Uninitialized members (UNINIT_CTOR) >>> Non-static class member "m_gradEndLoc" is not initialized in this constructor nor in any functions that it calls. 700 } 701 702 void QQuickShapeLinearGradientShader::initialize() 703 { 704 m_opacityLoc = program()->uniformLocation("opacity"); 705 m_matrixLoc = program()->uniformLocation("matrix"); Change-Id: Ic9435039a1409ade63c7592a4a55b6c7306d03c2 Coverity-Id: 181280 Reviewed-by: Laszlo Agocs --- src/imports/shapes/qquickshapegenericrenderer_p.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/imports/shapes/qquickshapegenericrenderer_p.h b/src/imports/shapes/qquickshapegenericrenderer_p.h index 430a95465b..ad2427f00a 100644 --- a/src/imports/shapes/qquickshapegenericrenderer_p.h +++ b/src/imports/shapes/qquickshapegenericrenderer_p.h @@ -259,10 +259,10 @@ public: static QSGMaterialType type; private: - int m_opacityLoc; - int m_matrixLoc; - int m_gradStartLoc; - int m_gradEndLoc; + int m_opacityLoc = -1; + int m_matrixLoc = -1; + int m_gradStartLoc = -1; + int m_gradEndLoc = -1; }; class QQuickShapeLinearGradientMaterial : public QSGMaterial -- cgit v1.2.3 From 31611f32bb89564dc6933d47eeb4ca4e45ac5563 Mon Sep 17 00:00:00 2001 From: Jesus Fernandez Date: Sun, 25 Jun 2017 11:16:17 +0200 Subject: Initialize uniform location variables *** CID 181276: Uninitialized members (UNINIT_CTOR) /qtdeclarative/src/imports/shapes/qquickshapenvprrenderer_p.h: 174 in QQuickNvprBlitter::QQuickNvprBlitter()() 168 float opacity); 169 170 private: 171 QOpenGLShaderProgram *m_program = nullptr; 172 QOpenGLBuffer *m_buffer = nullptr; 173 int m_matrixLoc; >>> CID 181276: Uninitialized members (UNINIT_CTOR) >>> The compiler-generated constructor for this class does not initialize "m_opacityLoc". 174 int m_opacityLoc; 175 QSize m_prevSize; 176 }; 177 178 class QQuickShapeNvprRenderNode : public QSGRenderNode 179 { Change-Id: I4176981fe79c175db974ff428a5116d82420f7a6 Coverity-Id: 181276 Reviewed-by: Laszlo Agocs --- src/imports/shapes/qquickshapenvprrenderer_p.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/imports/shapes/qquickshapenvprrenderer_p.h b/src/imports/shapes/qquickshapenvprrenderer_p.h index d90357a40d..159bf79493 100644 --- a/src/imports/shapes/qquickshapenvprrenderer_p.h +++ b/src/imports/shapes/qquickshapenvprrenderer_p.h @@ -170,8 +170,8 @@ public: private: QOpenGLShaderProgram *m_program = nullptr; QOpenGLBuffer *m_buffer = nullptr; - int m_matrixLoc; - int m_opacityLoc; + int m_matrixLoc = -1; + int m_opacityLoc = -1; QSize m_prevSize; }; -- cgit v1.2.3 From 21fd17d7c1fca77e802ba2d3fd5aeebcdef157e4 Mon Sep 17 00:00:00 2001 From: Jesus Fernandez Date: Sun, 25 Jun 2017 10:37:49 +0200 Subject: Add Q_ASSERT to test prevNode To make Coverity happy: *** CID 181275: Null pointer dereferences (FORWARD_NULL) /qtdeclarative/src/imports/shapes/qquickshapegenericrenderer.cpp: 503 in QQuickShapeGenericRenderer::updateNode()() 497 QQuickShapeGenericNode **nodePtr = &m_rootNode; 498 QQuickShapeGenericNode *prevNode = nullptr; 499 500 for (ShapePathData &d : m_sp) { 501 if (!*nodePtr) { 502 *nodePtr = new QQuickShapeGenericNode; >>> CID 181275: Null pointer dereferences (FORWARD_NULL) >>> Dereferencing null pointer "prevNode". 503 prevNode->m_next = *nodePtr; 504 prevNode->appendChildNode(*nodePtr); 505 } 506 507 QQuickShapeGenericNode *node = *nodePtr; 508 Change-Id: I76e79ef6fe94aa8b0e77f79724101b4682dab6d3 Coverity-Id: 181275 Reviewed-by: Laszlo Agocs --- src/imports/shapes/qquickshapegenericrenderer.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/imports/shapes/qquickshapegenericrenderer.cpp b/src/imports/shapes/qquickshapegenericrenderer.cpp index 47203698d5..4a4176793f 100644 --- a/src/imports/shapes/qquickshapegenericrenderer.cpp +++ b/src/imports/shapes/qquickshapegenericrenderer.cpp @@ -499,6 +499,7 @@ void QQuickShapeGenericRenderer::updateNode() for (ShapePathData &d : m_sp) { if (!*nodePtr) { + Q_ASSERT(prevNode); *nodePtr = new QQuickShapeGenericNode; prevNode->m_next = *nodePtr; prevNode->appendChildNode(*nodePtr); -- cgit v1.2.3 From 4beee1a6dcc1be57aa6fb2a175dadc6ff298545d Mon Sep 17 00:00:00 2001 From: Liang Qi Date: Mon, 26 Jun 2017 09:23:11 +0200 Subject: shapes: Convert QT_NO_OPENGL to QT_CONFIG(opengl) Also guard fillGradient for now. Task-number: QTBUG-61632 Change-Id: I5fa2607cc1ede0922519258afd455bee4d0187c7 Reviewed-by: Laszlo Agocs --- src/imports/shapes/qquicknvprfunctions.cpp | 4 ++-- src/imports/shapes/qquicknvprfunctions_p.h | 4 ++-- src/imports/shapes/qquickshape.cpp | 8 ++++---- src/imports/shapes/qquickshape_p_p.h | 4 ++-- src/imports/shapes/qquickshapegenericrenderer.cpp | 20 ++++++++++++++------ src/imports/shapes/qquickshapegenericrenderer_p.h | 8 ++++++-- src/imports/shapes/qquickshapenvprrenderer_p.h | 4 ++-- 7 files changed, 32 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/imports/shapes/qquicknvprfunctions.cpp b/src/imports/shapes/qquicknvprfunctions.cpp index e9b93cf4a1..0b92d4b4c0 100644 --- a/src/imports/shapes/qquicknvprfunctions.cpp +++ b/src/imports/shapes/qquicknvprfunctions.cpp @@ -39,7 +39,7 @@ #include "qquicknvprfunctions_p.h" -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) #include #include @@ -274,4 +274,4 @@ bool QQuickNvprFunctionsPrivate::resolve() QT_END_NAMESPACE -#endif // QT_NO_OPENGL +#endif // QT_CONFIG(opengl) diff --git a/src/imports/shapes/qquicknvprfunctions_p.h b/src/imports/shapes/qquicknvprfunctions_p.h index dd45dd7daa..342e92cbc3 100644 --- a/src/imports/shapes/qquicknvprfunctions_p.h +++ b/src/imports/shapes/qquicknvprfunctions_p.h @@ -54,7 +54,7 @@ #include #include -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) QT_BEGIN_NAMESPACE @@ -395,6 +395,6 @@ private: QT_END_NAMESPACE -#endif // QT_NO_OPENGL +#endif // QT_CONFIG(opengl) #endif // QQUICKNVPRFUNCTIONS_P_H diff --git a/src/imports/shapes/qquickshape.cpp b/src/imports/shapes/qquickshape.cpp index 4e6896621e..1054af30de 100644 --- a/src/imports/shapes/qquickshape.cpp +++ b/src/imports/shapes/qquickshape.cpp @@ -921,7 +921,7 @@ void QQuickShapePrivate::createRenderer() return; switch (ri->graphicsApi()) { -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) case QSGRendererInterface::OpenGL: if (enableVendorExts && QQuickShapeNvprRenderNode::isSupported()) { rendererType = QQuickShape::NvprRenderer; @@ -954,7 +954,7 @@ QSGNode *QQuickShapePrivate::createNode() return node; switch (ri->graphicsApi()) { -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) case QSGRendererInterface::OpenGL: if (enableVendorExts && QQuickShapeNvprRenderNode::isSupported()) { node = new QQuickShapeNvprRenderNode; @@ -1199,7 +1199,7 @@ void QQuickShapeLinearGradient::setY2(qreal v) } } -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) // contexts sharing with each other get the same cache instance class QQuickShapeGradientCacheWrapper @@ -1324,7 +1324,7 @@ QSGTexture *QQuickShapeGradientCache::get(const GradientDesc &grad) return tx; } -#endif // QT_NO_OPENGL +#endif // QT_CONFIG(opengl) QT_END_NAMESPACE diff --git a/src/imports/shapes/qquickshape_p_p.h b/src/imports/shapes/qquickshape_p_p.h index a8a5675ccb..888488efcd 100644 --- a/src/imports/shapes/qquickshape_p_p.h +++ b/src/imports/shapes/qquickshape_p_p.h @@ -234,7 +234,7 @@ private: QV4::PersistentValue m_v4value; }; -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) class QQuickShapeGradientCache : public QOpenGLSharedResource { @@ -274,7 +274,7 @@ inline uint qHash(const QQuickShapeGradientCache::GradientDesc &v, uint seed = 0 return h; } -#endif // QT_NO_OPENGL +#endif // QT_CONFIG(opengl) QT_END_NAMESPACE diff --git a/src/imports/shapes/qquickshapegenericrenderer.cpp b/src/imports/shapes/qquickshapegenericrenderer.cpp index 4a4176793f..ca5f2a0e91 100644 --- a/src/imports/shapes/qquickshapegenericrenderer.cpp +++ b/src/imports/shapes/qquickshapegenericrenderer.cpp @@ -42,7 +42,7 @@ #include #include -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) #include #include #include @@ -118,7 +118,7 @@ void QQuickShapeGenericStrokeFillNode::activateMaterial(Material m) static bool q_supportsElementIndexUint(QSGRendererInterface::GraphicsApi api) { static bool elementIndexUint = true; -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) if (api == QSGRendererInterface::OpenGL) { static bool elementIndexUintChecked = false; if (!elementIndexUintChecked) { @@ -246,6 +246,7 @@ void QQuickShapeGenericRenderer::setFillGradient(int index, QQuickShapeGradient { ShapePathData &d(m_sp[index]); d.fillGradientActive = gradient != nullptr; +#if QT_CONFIG(opengl) if (gradient) { d.fillGradient.stops = gradient->gradientStops(); // sorted d.fillGradient.spread = gradient->spread(); @@ -256,6 +257,7 @@ void QQuickShapeGenericRenderer::setFillGradient(int index, QQuickShapeGradient Q_UNREACHABLE(); } } +#endif d.syncDirty |= DirtyFillGradient; } @@ -559,8 +561,12 @@ void QQuickShapeGenericRenderer::updateNode() void QQuickShapeGenericRenderer::updateShadowDataInNode(ShapePathData *d, QQuickShapeGenericStrokeFillNode *n) { if (d->fillGradientActive) { +#if QT_CONFIG(opengl) if (d->effectiveDirty & DirtyFillGradient) n->m_fillGradient = d->fillGradient; +#else + Q_UNUSED(n); +#endif } } @@ -665,7 +671,7 @@ QSGMaterial *QQuickShapeGenericMaterialFactory::createVertexColor(QQuickWindow * { QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... return new QSGVertexColorMaterial; #endif @@ -679,16 +685,18 @@ QSGMaterial *QQuickShapeGenericMaterialFactory::createLinearGradient(QQuickWindo { QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... return new QQuickShapeLinearGradientMaterial(node); +#else + Q_UNUSED(node); #endif qWarning("Linear gradient material: Unsupported graphics API %d", api); return nullptr; } -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) QSGMaterialType QQuickShapeLinearGradientShader::type; @@ -771,6 +779,6 @@ int QQuickShapeLinearGradientMaterial::compare(const QSGMaterial *other) const return 0; } -#endif // QT_NO_OPENGL +#endif // QT_CONFIG(opengl) QT_END_NAMESPACE diff --git a/src/imports/shapes/qquickshapegenericrenderer_p.h b/src/imports/shapes/qquickshapegenericrenderer_p.h index ad2427f00a..1f36e3decf 100644 --- a/src/imports/shapes/qquickshapegenericrenderer_p.h +++ b/src/imports/shapes/qquickshapegenericrenderer_p.h @@ -133,7 +133,9 @@ private: Qt::FillRule fillRule; QPainterPath path; bool fillGradientActive; +#if QT_CONFIG(opengl) QQuickShapeGradientCache::GradientDesc fillGradient; +#endif VertexContainerType fillVertices; IndexContainerType fillIndices; QSGGeometry::Type indexType; @@ -218,7 +220,9 @@ public: QQuickWindow *window() const { return m_window; } // shadow data for custom materials +#if QT_CONFIG(opengl) QQuickShapeGradientCache::GradientDesc m_fillGradient; +#endif private: QSGGeometry *m_geometry; @@ -245,7 +249,7 @@ public: static QSGMaterial *createLinearGradient(QQuickWindow *window, QQuickShapeGenericStrokeFillNode *node); }; -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) class QQuickShapeLinearGradientShader : public QSGMaterialShader { @@ -297,7 +301,7 @@ private: QQuickShapeGenericStrokeFillNode *m_node; }; -#endif // QT_NO_OPENGL +#endif // QT_CONFIG(opengl) QT_END_NAMESPACE diff --git a/src/imports/shapes/qquickshapenvprrenderer_p.h b/src/imports/shapes/qquickshapenvprrenderer_p.h index 159bf79493..ec7ba498f9 100644 --- a/src/imports/shapes/qquickshapenvprrenderer_p.h +++ b/src/imports/shapes/qquickshapenvprrenderer_p.h @@ -58,7 +58,7 @@ #include #include -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) QT_BEGIN_NAMESPACE @@ -232,6 +232,6 @@ private: QT_END_NAMESPACE -#endif // QT_NO_OPENGL +#endif // QT_CONFIG(opengl) #endif // QQUICKSHAPENVPRRENDERER_P_H -- cgit v1.2.3