aboutsummaryrefslogtreecommitdiffstats
path: root/src/quickshapes/qquickshape.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quickshapes/qquickshape.cpp')
-rw-r--r--src/quickshapes/qquickshape.cpp591
1 files changed, 387 insertions, 204 deletions
diff --git a/src/quickshapes/qquickshape.cpp b/src/quickshapes/qquickshape.cpp
index 50e995f07e..96f83b9af6 100644
--- a/src/quickshapes/qquickshape.cpp
+++ b/src/quickshapes/qquickshape.cpp
@@ -1,57 +1,22 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickshape_p.h"
#include "qquickshape_p_p.h"
#include "qquickshapegenericrenderer_p.h"
#include "qquickshapesoftwarerenderer_p.h"
+#include "qquickshapecurverenderer_p.h"
#include <private/qsgplaintexture_p.h>
#include <private/qquicksvgparser_p.h>
#include <QtGui/private/qdrawhelper_p.h>
#include <QOpenGLFunctions>
#include <QLoggingCategory>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
static void initResources()
{
#if defined(QT_STATIC)
- Q_INIT_RESOURCE(qtquickshapes);
+ Q_INIT_RESOURCE(qtquickshapes_shaders);
#endif
}
@@ -70,6 +35,35 @@ Q_LOGGING_CATEGORY(QQSHAPE_LOG_TIME_DIRTY_SYNC, "qt.shape.time.sync")
\qml
import QtQuick.Shapes
\endqml
+
+ Qt Quick Shapes provides tools for drawing arbitrary shapes in a Qt Quick scene.
+ \l{Shape}{Shapes} can be constructed from basic building blocks like \l{PathLine}{lines} and
+ \l{PathCubic}{curves} that define sub-shapes. The sub-shapes can then be filled with solid
+ colors or gradients, and an outline stroke can be defined.
+
+ Qt Quick Shapes also supports higher level path element types, such as \l{PathText}{text} and
+ \l{PathSvg}{SVG path descriptions}. The currently supported element types is: PathMove,
+ PathLine, PathQuad, PathCubic, PathArc, PathText and PathSvg.
+
+ Qt Quick Shapes triangulates the shapes and renders the corresponding triangles on the GPU.
+ Therefore, altering the control points of elements will lead to re-triangulation of the
+ affected paths, at some performance cost. In addition, curves are flattened before they are
+ rendered, so applying a very high scale to the shape may show artifacts where it is visible
+ that the curves are represented by a sequence of smaller, straight lines.
+
+ \note By default, Qt Quick Shapes relies on multi-sampling for anti-aliasing. This can be
+ enabled for the entire application or window using the corresponding settings in QSurfaceFormat.
+ It can also be enabled for only the shape, by setting its \l{Item::layer.enabled}{layer.enabled}
+ property to true and then adjusting the \l{Item::layer.samples}{layer.samples} property. In the
+ latter case, multi-sampling will not be applied to the entire scene, but the shape will be
+ rendered via an intermediate off-screen buffer. Alternatively, the
+ \l{QtQuick.Shapes::Shape::preferredRendererType}{preferredRendererType} property can be set
+ to \c{Shape.CurveRenderer}. This has anti-aliasing built in and generally renders the shapes
+ at a higher quality, but at some additional performance cost.
+
+ For further information, the \l{Qt Quick Examples - Shapes}{Shapes example} shows how to
+ implement different types of shapes, fills and strokes, and the \l{Weather Forecast Example}
+ shows examples of different ways shapes might be useful in a user interface.
*/
void QQuickShapes_initializeModule()
@@ -155,7 +149,7 @@ QQuickShapeStrokeFillParams::QQuickShapeStrokeFillParams()
\image visualpath-code-example.png
- \sa {Qt Quick Examples - Shapes}, Shape
+ \sa {Qt Quick Examples - Shapes}, {Weather Forecast Example}, Shape
*/
QQuickShapePathPrivate::QQuickShapePathPrivate()
@@ -396,10 +390,8 @@ void QQuickShapePath::setCapStyle(CapStyle style)
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
+ \value ShapePath.SolidLine A plain line.
+ \value ShapePath.DashLine Dashes separated by a few pixels.
*/
QQuickShapePath::StrokeStyle QQuickShapePath::strokeStyle() const
@@ -527,6 +519,57 @@ void QQuickShapePath::resetFillGradient()
}
/*!
+ \qmlproperty PathHints QtQuick.Shapes::ShapePath::pathHints
+ \since 6.7
+
+ This property describes characteristics of the shape. If set, these hints may allow
+ optimized rendering. By default, no hints are set. It can be a combination of the following
+ values:
+
+ \value ShapePath.PathLinear
+ The path only has straight lines, no curves.
+ \value ShapePath.PathQuadratic
+ The path does not have any cubic curves: only lines and quadratic Bezier curves.
+ \value ShapePath.PathConvex
+ The path does not have any dents or holes. All straight lines between two points
+ inside the shape will be completely inside the shape.
+ \value ShapePath.PathFillOnRight
+ The path follows the TrueType convention where outlines around solid fill have their
+ control points ordered clockwise, and outlines around holes in the shape have their
+ control points ordered counter-clockwise.
+ \value ShapePath.PathSolid
+ The path has no holes, or mathematically speaking it is \e{simply connected}.
+ \value ShapePath.PathNonIntersecting
+ The path outline does not cross itself.
+ \value ShapePath.PathNonOverlappingControlPointTriangles
+ The triangles defined by the curve control points do not overlap with each other,
+ or with any of the line segments. Also, no line segments intersect.
+ This implies \c PathNonIntersecting.
+
+ Not all hints are logically independent, but the dependencies are not enforced.
+ For example, \c PathIsLinear implies \c PathIsQuadratic, but it is valid to have \c PathIsLinear
+ without \c PathIsQuadratic.
+
+ The pathHints property describes a set of statements known to be true; the absence of a hint
+ does not necessarily mean that the corresponding statement is false.
+*/
+
+QQuickShapePath::PathHints QQuickShapePath::pathHints() const
+{
+ Q_D(const QQuickShapePath);
+ return d->pathHints;
+}
+
+void QQuickShapePath::setPathHints(PathHints newPathHints)
+{
+ Q_D(QQuickShapePath);
+ if (d->pathHints == newPathHints)
+ return;
+ d->pathHints = newPathHints;
+ emit pathHintsChanged();
+}
+
+/*!
\qmltype Shape
//! \instantiates QQuickShape
\inqmlmodule QtQuick.Shapes
@@ -536,9 +579,7 @@ void QQuickShapePath::resetFillGradient()
\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}.
+ Renders a path by triangulating geometry from a QPainterPath.
This approach is different from rendering shapes via QQuickPaintedItem or
the 2D Canvas because the path never gets rasterized in software.
@@ -552,7 +593,7 @@ void QQuickShapePath::resetFillGradient()
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, and PathSvg.
+ PathArc, PathText and PathSvg.
See \l Path for a detailed overview of the supported path elements.
@@ -594,14 +635,9 @@ void QQuickShapePath::resetFillGradient()
\list
- \li When running with the OpenGL backend of Qt Quick, both the generic,
- triangulation-based and the NVIDIA-specific \c{GL_NV_path_rendering}
- methods are available. By default only the generic approach is used.
- Setting Shape.vendorExtensionsEnabled property to \c true leads to using
- NV_path_rendering on NVIDIA systems when running directly on OpenGL, and
- the generic method on others. When OpenGL is not used directly by the scene
- graph, for example because it is using the graphics abstraction layer
- (QRhi), only the generic shape renderer is available.
+ \li When Qt Quick is running with the default, hardware-accelerated backend (RHI),
+ the generic shape renderer will be used. This converts the shapes into triangles
+ which are passed to the renderer.
\li The \c software backend is fully supported. The path is rendered via
QPainter::strokePath() and QPainter::fillPath() in this case.
@@ -630,16 +666,10 @@ void QQuickShapePath::resetFillGradient()
a frequently changing property can still result in a lower overall system
load than with imperative painting approaches (for example, QPainter).
- \li 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 property changes is smaller.
-
\li At the same time, attention must be paid to the number of Shape
- elements in the scene, in particular when using this special accelerated
- approach for \c{GL_NV_path_rendering}. The way such a Shape item is
- represented in the scene graph is different from an ordinary
- geometry-based item, and incurs a certain cost when it comes to OpenGL
- state changes.
+ elements in the scene. The way such a Shape item is represented in
+ the scene graph is different from an ordinary geometry-based item,
+ and incurs a certain cost when it comes to OpenGL state changes.
\li As a general rule, scenes should avoid using separate Shape items when
it is not absolutely necessary. Prefer using one Shape item with multiple
@@ -647,7 +677,7 @@ void QQuickShapePath::resetFillGradient()
\endlist
- \sa {Qt Quick Examples - Shapes}, Path, PathMove, PathLine, PathQuad, PathCubic, PathArc, PathSvg
+ \sa {Qt Quick Examples - Shapes}, {Weather Forecast Example}, Path, PathMove, PathLine, PathQuad, PathCubic, PathArc, PathSvg
*/
QQuickShapePrivate::QQuickShapePrivate()
@@ -665,6 +695,9 @@ void QQuickShapePrivate::_q_shapePathChanged()
Q_Q(QQuickShape);
spChanged = true;
q->polish();
+ emit q->boundingRectChanged();
+ auto br = q->boundingRect();
+ q->setImplicitSize(br.right(), br.bottom());
}
void QQuickShapePrivate::setStatus(QQuickShape::Status newStatus)
@@ -676,6 +709,18 @@ void QQuickShapePrivate::setStatus(QQuickShape::Status newStatus)
}
}
+qreal QQuickShapePrivate::getImplicitWidth() const
+{
+ Q_Q(const QQuickShape);
+ return q->boundingRect().right();
+}
+
+qreal QQuickShapePrivate::getImplicitHeight() const
+{
+ Q_Q(const QQuickShape);
+ return q->boundingRect().bottom();
+}
+
QQuickShape::QQuickShape(QQuickItem *parent)
: QQuickItem(*(new QQuickShapePrivate), parent)
{
@@ -688,6 +733,7 @@ QQuickShape::~QQuickShape()
/*!
\qmlproperty enumeration QtQuick.Shapes::Shape::rendererType
+ \readonly
This property determines which path rendering backend is active.
@@ -695,15 +741,33 @@ QQuickShape::~QQuickShape()
The renderer is unknown.
\value Shape.GeometryRenderer
- The generic, driver independent solution for OpenGL. Uses the same
+ The generic, driver independent solution for GPU rendering. 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.
+ engine. This is the default when the RHI-based Qt Quick scenegraph
+ backend is in use.
\value 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.
+
+ \value Shape.CurveRenderer
+ GPU-based renderer that aims to preserve curvature at any scale.
+ In contrast to \c Shape.GeometryRenderer, curves are not approximated by short straight
+ lines. Instead, curves are rendered using a specialized fragment shader. This improves
+ visual quality and avoids re-tesselation performance hit when zooming. Also,
+ \c Shape.CurveRenderer provides native, high-quality anti-aliasing, without the
+ performance cost of multi- or supersampling.
+
+ By default, \c Shape.GeometryRenderer will be selected unless the Qt Quick scenegraph is running
+ with the \c software backend. In that case, \c Shape.SoftwareRenderer will be used.
+ \c Shape.CurveRenderer may be requested using the \l preferredRendererType property.
+
+ \note The \c Shape.CurveRenderer will approximate cubic curves with quadratic ones and may
+ therefore diverge slightly from the mathematically correct visualization of the shape. In
+ addition, if the shape is being rendered into a Qt Quick 3D scene and the OpenGL backend for
+ RHI is active, the \c GL_OES_standard_derivatives extension to OpenGL is required (this is
+ available by default on OpenGL ES 3 and later, but optional in OpenGL ES 2.)
*/
QQuickShape::RendererType QQuickShape::rendererType() const
@@ -713,12 +777,58 @@ QQuickShape::RendererType QQuickShape::rendererType() const
}
/*!
+ \qmlproperty enumeration QtQuick.Shapes::Shape::preferredRendererType
+ \since 6.6
+
+ Requests a specific backend to use for rendering the shape. The possible values are the same as
+ for \l rendererType. The default is \c Shape.UnknownRenderer, indicating no particular preference.
+
+ If the requested renderer type is not supported for the current Qt Quick backend, the default
+ renderer for that backend will be used instead. This will be reflected in the \l rendererType
+ when the backend is initialized.
+
+ \c Shape.SoftwareRenderer can currently not be selected without running the scenegraph with
+ the \c software backend, in which case it will be selected regardless of the
+ \c preferredRendererType.
+
+ See \l rendererType for more information on the implications.
+*/
+
+QQuickShape::RendererType QQuickShape::preferredRendererType() const
+{
+ Q_D(const QQuickShape);
+ return d->preferredType;
+}
+
+void QQuickShape::setPreferredRendererType(QQuickShape::RendererType preferredType)
+{
+ Q_D(QQuickShape);
+ if (d->preferredType == preferredType)
+ return;
+
+ d->preferredType = preferredType;
+ // (could bail out here if selectRenderType shows no change?)
+
+ for (int i = 0; i < d->sp.size(); ++i) {
+ QQuickShapePath *p = d->sp[i];
+ QQuickShapePathPrivate *pp = QQuickShapePathPrivate::get(p);
+ pp->dirty |= QQuickShapePathPrivate::DirtyAll;
+ }
+ d->spChanged = true;
+ d->_q_shapePathChanged();
+ polish();
+ update();
+
+ emit preferredRendererTypeChanged();
+}
+
+/*!
\qmlproperty bool QtQuick.Shapes::Shape::asynchronous
- When rendererType is \c 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 rendererType is \c Shape.GeometryRenderer or \c Shape.CurveRenderer, a certain amount of
+ preprocessing of the input path is performed 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
@@ -746,17 +856,32 @@ void QQuickShape::setAsynchronous(bool async)
}
/*!
+ \qmlproperty rect QtQuick.Shapes::Shape::boundingRect
+ \readonly
+ \since 6.6
+
+ Contains the united bounding rect of all sub paths in the shape.
+ */
+QRectF QQuickShape::boundingRect() const
+{
+ Q_D(const QQuickShape);
+ QRectF brect;
+ for (QQuickShapePath *path : d->sp) {
+ qreal pw = path->strokeColor().alpha() ? path->strokeWidth() : 0;
+ qreal d = path->capStyle() == QQuickShapePath::SquareCap ? pw * M_SQRT1_2 : pw / 2;
+ brect = brect.united(path->path().boundingRect().adjusted(-d, -d, d, d));
+ }
+
+ return brect;
+}
+
+/*!
\qmlproperty bool QtQuick.Shapes::Shape::vendorExtensionsEnabled
- This property controls the usage of non-standard OpenGL extensions like
- \c GL_NV_path_rendering.
+ This property controls the usage of non-standard OpenGL extensions.
The default value is \c false.
- As of Qt 5.12 Shape.NvprRenderer is disabled by default and a uniform
- behavior, based on triangulating the path and generating QSGGeometryNode
- instances, is used regardless of the graphics card and drivers.
-
As of Qt 6.0 there are no vendor-specific rendering paths implemented.
*/
@@ -777,6 +902,7 @@ void QQuickShape::setVendorExtensionsEnabled(bool enable)
/*!
\qmlproperty enumeration QtQuick.Shapes::Shape::status
+ \readonly
This property determines the status of the Shape and is relevant when
Shape.asynchronous is set to \c true.
@@ -853,6 +979,80 @@ bool QQuickShape::contains(const QPointF &point) const
return false;
}
+/*!
+ \qmlproperty enumeration QtQuick.Shapes::Shape::fillMode
+ \since QtQuick.Shapes 6.7
+
+ Set this property to define what happens when the path has a different size
+ than the item.
+
+ \value Shape.NoResize the shape is rendered at its native size, independent of the size of the item. This is the default
+ \value Shape.Stretch the shape is scaled to fit the item, changing the aspect ratio if necessary.
+ Note that non-uniform scaling may cause reduced quality of anti-aliasing when using the curve renderer
+ \value Shape.PreserveAspectFit the shape is scaled uniformly to fit inside the item
+ \value Shape.PreserveAspectCrop the shape is scaled uniformly to fill the item fully, extending outside the item if necessary.
+ Note that this only actually crops the content if \l clip is true
+*/
+
+QQuickShape::FillMode QQuickShape::fillMode() const
+{
+ Q_D(const QQuickShape);
+ return d->fillMode;
+}
+
+void QQuickShape::setFillMode(FillMode newFillMode)
+{
+ Q_D(QQuickShape);
+ if (d->fillMode == newFillMode)
+ return;
+ d->fillMode = newFillMode;
+ emit fillModeChanged();
+}
+
+/*!
+ \qmlproperty enumeration QtQuick.Shapes::Shape::horizontalAlignment
+ \qmlproperty enumeration QtQuick.Shapes::Shape::verticalAlignment
+ \since 6.7
+
+ Sets the horizontal and vertical alignment of the shape within the item.
+ By default, the shape is aligned with \c{(0,0)} on the top left corner.
+
+ The valid values for \c horizontalAlignment are \c Shape.AlignLeft,
+ \c Shape.AlignRight and \c Shape.AlignHCenter. The valid values for
+ \c verticalAlignment are \c Shape.AlignTop, \c Shape.AlignBottom and
+ \c Shape.AlignVCenter.
+*/
+
+QQuickShape::HAlignment QQuickShape::horizontalAlignment() const
+{
+ Q_D(const QQuickShape);
+ return d->horizontalAlignment;
+}
+
+void QQuickShape::setHorizontalAlignment(HAlignment newHorizontalAlignment)
+{
+ Q_D(QQuickShape);
+ if (d->horizontalAlignment == newHorizontalAlignment)
+ return;
+ d->horizontalAlignment = newHorizontalAlignment;
+ emit horizontalAlignmentChanged();
+}
+
+QQuickShape::VAlignment QQuickShape::verticalAlignment() const
+{
+ Q_D(const QQuickShape);
+ return d->verticalAlignment;
+}
+
+void QQuickShape::setVerticalAlignment(VAlignment newVerticalAlignment)
+{
+ Q_D(QQuickShape);
+ if (d->verticalAlignment == newVerticalAlignment)
+ return;
+ d->verticalAlignment = newVerticalAlignment;
+ emit verticalAlignmentChanged();
+}
+
static void vpe_append(QQmlListProperty<QObject> *property, QObject *obj)
{
QQuickShape *item = static_cast<QQuickShape *>(property->object);
@@ -892,7 +1092,7 @@ static void vpe_clear(QQmlListProperty<QObject> *property)
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
+ \qmldefault
*/
QQmlListProperty<QObject> QQuickShape::data()
@@ -933,6 +1133,12 @@ void QQuickShape::updatePolish()
d->spChanged = false;
d->effectRefCount = currentEffectRefCount;
+ QQuickShape::RendererType expectedRenderer = d->selectRendererType();
+ if (d->rendererType != expectedRenderer) {
+ delete d->renderer;
+ d->renderer = nullptr;
+ }
+
if (!d->renderer) {
d->createRenderer();
if (!d->renderer)
@@ -955,7 +1161,7 @@ void QQuickShape::itemChange(ItemChange change, const ItemChangeData &data)
if (change == ItemVisibleHasChanged && data.boolValue)
d->_q_shapePathChanged();
else if (change == QQuickItem::ItemSceneChange) {
- for (int i = 0; i < d->sp.count(); ++i)
+ for (int i = 0; i < d->sp.size(); ++i)
QQuickShapePathPrivate::get(d->sp[i])->dirty = QQuickShapePathPrivate::DirtyAll;
d->_q_shapePathChanged();
}
@@ -967,38 +1173,108 @@ 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)
+
+ if (d->renderer || d->rendererChanged) {
+ if (!node || d->rendererChanged) {
+ d->rendererChanged = false;
+ delete node;
node = d->createNode();
- d->renderer->updateNode();
+ }
+ if (d->renderer)
+ d->renderer->updateNode();
+
+ // TODO: only add transform node when needed (and then make sure static_cast is safe)
+ QMatrix4x4 fillModeTransform;
+ qreal xScale = 1.0;
+ qreal yScale = 1.0;
+
+ if (d->fillMode != NoResize) {
+ xScale = width() / implicitWidth();
+ yScale = height() / implicitHeight();
+
+ if (d->fillMode == PreserveAspectFit)
+ xScale = yScale = qMin(xScale, yScale);
+ else if (d->fillMode == PreserveAspectCrop)
+ xScale = yScale = qMax(xScale, yScale);
+ fillModeTransform.scale(xScale, yScale);
+ }
+ if (d->horizontalAlignment != AlignLeft || d->verticalAlignment != AlignTop) {
+ qreal tx = 0;
+ qreal ty = 0;
+ qreal w = xScale * implicitWidth();
+ qreal h = yScale * implicitHeight();
+ if (d->horizontalAlignment == AlignRight)
+ tx = width() - w;
+ else if (d->horizontalAlignment == AlignHCenter)
+ tx = (width() - w) / 2;
+ if (d->verticalAlignment == AlignBottom)
+ ty = height() - h;
+ else if (d->verticalAlignment == AlignVCenter)
+ ty = (height() - h) / 2;
+ fillModeTransform.translate(tx / xScale, ty / yScale);
+ }
+ static_cast<QSGTransformNode *>(node)->setMatrix(fillModeTransform);
}
return node;
}
-// the renderer object lives on the gui thread
-void QQuickShapePrivate::createRenderer()
+QQuickShape::RendererType QQuickShapePrivate::selectRendererType()
{
+ QQuickShape::RendererType res = QQuickShape::UnknownRenderer;
Q_Q(QQuickShape);
QSGRendererInterface *ri = q->window()->rendererInterface();
if (!ri)
- return;
+ return res;
+
+ static const bool environmentPreferCurve =
+ qEnvironmentVariable("QT_QUICKSHAPES_BACKEND").toLower() == QLatin1String("curverenderer");
switch (ri->graphicsApi()) {
case QSGRendererInterface::Software:
- rendererType = QQuickShape::SoftwareRenderer;
- renderer = new QQuickShapeSoftwareRenderer;
+ res = QQuickShape::SoftwareRenderer;
break;
default:
if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) {
- rendererType = QQuickShape::GeometryRenderer;
- renderer = new QQuickShapeGenericRenderer(q);
+ if (preferredType == QQuickShape::CurveRenderer || environmentPreferCurve) {
+ res = QQuickShape::CurveRenderer;
+ } else {
+ res = QQuickShape::GeometryRenderer;
+ }
} else {
qWarning("No path backend for this graphics API yet");
}
break;
}
+
+ return res;
+}
+
+// the renderer object lives on the gui thread
+void QQuickShapePrivate::createRenderer()
+{
+ Q_Q(QQuickShape);
+ QQuickShape::RendererType selectedType = selectRendererType();
+ if (selectedType == QQuickShape::UnknownRenderer)
+ return;
+
+ rendererType = selectedType;
+ rendererChanged = true;
+
+ switch (selectedType) {
+ case QQuickShape::SoftwareRenderer:
+ renderer = new QQuickShapeSoftwareRenderer;
+ break;
+ case QQuickShape::GeometryRenderer:
+ renderer = new QQuickShapeGenericRenderer(q);
+ break;
+ case QQuickShape::CurveRenderer:
+ renderer = new QQuickShapeCurveRenderer(q);
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
}
// the node lives on the render thread
@@ -1006,29 +1282,39 @@ QSGNode *QQuickShapePrivate::createNode()
{
Q_Q(QQuickShape);
QSGNode *node = nullptr;
- if (!q->window())
+ if (!q->window() || !renderer)
return node;
QSGRendererInterface *ri = q->window()->rendererInterface();
if (!ri)
return node;
+ QSGNode *pathNode = nullptr;
switch (ri->graphicsApi()) {
case QSGRendererInterface::Software:
- node = new QQuickShapeSoftwareRenderNode(q);
+ pathNode = new QQuickShapeSoftwareRenderNode(q);
static_cast<QQuickShapeSoftwareRenderer *>(renderer)->setNode(
- static_cast<QQuickShapeSoftwareRenderNode *>(node));
+ static_cast<QQuickShapeSoftwareRenderNode *>(pathNode));
break;
default:
if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) {
- node = new QQuickShapeGenericNode;
- static_cast<QQuickShapeGenericRenderer *>(renderer)->setRootNode(
- static_cast<QQuickShapeGenericNode *>(node));
+ if (rendererType == QQuickShape::CurveRenderer) {
+ pathNode = new QSGNode;
+ static_cast<QQuickShapeCurveRenderer *>(renderer)->setRootNode(pathNode);
+ } else {
+ pathNode = new QQuickShapeGenericNode;
+ static_cast<QQuickShapeGenericRenderer *>(renderer)->setRootNode(
+ static_cast<QQuickShapeGenericNode *>(pathNode));
+ }
} else {
qWarning("No path backend for this graphics API yet");
}
break;
}
+ // TODO: only create transform node when needed
+ node = new QSGTransformNode;
+ node->appendChildNode(pathNode);
+
return node;
}
@@ -1054,9 +1340,10 @@ void QQuickShapePrivate::sync()
renderer->setAsyncCallback(asyncShapeReady, this);
}
- const int count = sp.count();
+ const int count = sp.size();
bool countChanged = false;
renderer->beginSync(count, &countChanged);
+ renderer->setTriangulationScale(triangulationScale);
for (int i = 0; i < count; ++i) {
QQuickShapePath *p = sp[i];
@@ -1487,110 +1774,6 @@ void QQuickShapeConicalGradient::setAngle(qreal v)
}
}
-static void generateGradientColorTable(const QQuickShapeGradientCacheKey &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;
-}
-
-QQuickShapeGradientCache::~QQuickShapeGradientCache()
-{
- qDeleteAll(m_textures);
-}
-
-QQuickShapeGradientCache *QQuickShapeGradientCache::cacheForRhi(QRhi *rhi)
-{
- static QHash<QRhi *, QQuickShapeGradientCache *> caches;
- auto it = caches.constFind(rhi);
- if (it != caches.constEnd())
- return *it;
-
- QQuickShapeGradientCache *cache = new QQuickShapeGradientCache;
- rhi->addCleanupCallback([cache](QRhi *rhi) {
- caches.remove(rhi);
- delete cache;
- });
- caches.insert(rhi, cache);
- return cache;
-}
-
-QSGTexture *QQuickShapeGradientCache::get(const QQuickShapeGradientCacheKey &grad)
-{
- QSGPlainTexture *tx = m_textures[grad];
- if (!tx) {
- static const int W = 1024; // texture size is 1024x1
- QImage gradTab(W, 1, QImage::Format_RGBA8888_Premultiplied);
- generateGradientColorTable(grad, reinterpret_cast<uint *>(gradTab.bits()), W, 1.0f);
- tx = new QSGPlainTexture;
- tx->setImage(gradTab);
- 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;
- }
- tx->setFiltering(QSGTexture::Linear);
- m_textures[grad] = tx;
- }
- return tx;
-}
-
QT_END_NAMESPACE
#include "moc_qquickshape_p.cpp"