Introduce hardware accelerated curve renderer for Shapes

This implements the Loop/Blinn algorithm for quadratic curves
as an optional backend for Qt Quick Shapes, basically distance
fields where the distance to curves are calculated in the
fragment shader.

This means cubic curves are approximated, which will give
varying results, but for many shapes (such as text) this is
efficient and means the shapes can be zoomed indefinitely
while still retaining curvature as well as anti-aliasing
working without MSAA.

Preliminary results give some frame rate improvements
compared to doing MSAA and GeometryRenderer, but the major
improvement is that you can get smooth curves at any zoom
level without re-triangulating the shape.

Note that the renderer currently does not do antialiasing
for straight lines. This would still require MSAA, but at
a lower cost than for GeometryRenderer since there are
much fewer triangles. Adding AA here as well is work in
progress.

Task-number: QTBUG-104122
Done-with: Paul Olav Tvete <paul.tvete@qt.io>
Done-with: Eirik Aavitsland <eirik.aavitsland@qt.io>
Done-with: Amr Elsayed <amr.elsayed@qt.io>
Change-Id: I6b4a1103546fbdfe760906f7a183101f8eedb9d3
Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
This commit is contained in:
Eskil Abrahamsen Blomfeldt 2022-09-06 12:45:55 +02:00
parent f6e5a11f0c
commit be813b9955
61 changed files with 11648 additions and 31 deletions

View File

@ -56,7 +56,7 @@ Rectangle {
property Component shapeType: Component {
ShapePath {
id: quadShapePath
strokeColor: root.palette.windowText
strokeColor: strokeSwitch.checked ? root.palette.windowText : "transparent"
strokeWidth: widthSlider.value
fillColor: fillSwitch.checked ? "green" : "transparent"
PathQuad {
@ -83,7 +83,7 @@ Rectangle {
property Component shapeType: Component {
ShapePath {
id: cubicShapePath
strokeColor: root.palette.windowText
strokeColor: strokeSwitch.checked ? root.palette.windowText : "transparent"
strokeWidth: widthSlider.value
fillColor: fillSwitch.checked ? "green" : "transparent"
PathCubic {
@ -109,6 +109,15 @@ Rectangle {
}
}
}
ToolButton {
id: modifyButton
text: qsTr("Modify")
checkable: true
onCheckedChanged: {
if (checked)
showHandlesSwitch.checked = true;
}
}
}
Label {
@ -130,6 +139,12 @@ Rectangle {
id: fillSwitch
text: qsTr("Fill")
}
Switch {
id: strokeSwitch
text: qsTr("Stroke")
checked: true
}
}
Component {
@ -144,9 +159,11 @@ Rectangle {
width: 20
height: width
radius: halfWidth
visible: showHandlesSwitch.checked
color: hh.hovered ? "yellow" : idleColor
border.color: "grey"
opacity: 0.75
property real halfWidth: width / 2
property bool complete: false
@ -203,14 +220,16 @@ Rectangle {
property ShapePath activePath: null
onActiveChanged: {
const tool = toolButtons.checkedButton;
if (active) {
activePath = tool.shapeType.createObject(root, {
startX: centroid.position.x, startY: centroid.position.y
});
shape.data.push(activePath);
} else {
activePath.finishCreation();
activePath = null;
if (tool != modifyButton) {
if (active) {
activePath = tool.shapeType.createObject(root, {
startX: centroid.position.x, startY: centroid.position.y
});
shape.data.push(activePath);
} else {
activePath.finishCreation();
activePath = null;
}
}
}
onCentroidChanged: if (activePath) {

View File

@ -159,8 +159,8 @@ Rectangle {
}
color: "darkBlue"
font.pointSize: 12
readonly property variant rendererStrings: [ qsTr("Unknown"), qsTr("Generic (QtGui triangulator)"), qsTr("GL_NV_path_rendering"), qsTr("Software (QPainter)") ]
text: qsTr("Active Shape backend: ") + rendererStrings[dummyShape.rendererType]
readonly property variant rendererStrings: [ qsTr("Unknown"), qsTr("Generic (QtGui triangulator)"), qsTr("GL_NV_path_rendering"), qsTr("Software (QPainter)"), qsTr("Curve Renderer") ]
text: "Active Shape backend: " + rendererStrings[dummyShape.rendererType]
SequentialAnimation on opacity {
NumberAnimation {
from: 1

View File

@ -372,6 +372,9 @@ void QQuickPath::processPath()
d->_path = createPath(QPointF(), QPointF(), d->_attributes, d->pathLength, d->_attributePoints, &d->closed);
}
if (d->simplified)
d->_path = d->_path.simplified();
emit changed();
}
@ -712,6 +715,32 @@ void QQuickPath::invalidateSequentialHistory() const
d->prevBez.isValid = false;
}
/*! \qmlproperty bool QtQuick::Path::simplified
\since 6.6
When set to true, the path will be simplified. This implies merging all subpaths that intersect,
creating a path where there are no self-intersections. Consecutive parallel lines will also be
merged. The simplified path is intended to be used with ShapePath.OddEvenFill. Bezier curves may
be flattened to line segments due to numerical instability of doing bezier curve intersections.
*/
void QQuickPath::setSimplified(bool simplified)
{
Q_D(QQuickPath);
if (d->simplified == simplified)
return;
d->simplified = simplified;
processPath();
emit simplifiedChanged();
}
bool QQuickPath::simplified() const
{
Q_D(const QQuickPath);
return d->simplified;
}
/*!
\qmlproperty size QtQuick::Path::scale

View File

@ -487,6 +487,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPath : public QObject, public QQmlParserStatu
Q_PROPERTY(qreal startX READ startX WRITE setStartX NOTIFY startXChanged)
Q_PROPERTY(qreal startY READ startY WRITE setStartY NOTIFY startYChanged)
Q_PROPERTY(bool closed READ isClosed NOTIFY changed)
Q_PROPERTY(bool simplified READ simplified WRITE setSimplified NOTIFY simplifiedChanged REVISION(6, 6))
Q_PROPERTY(QSizeF scale READ scale WRITE setScale NOTIFY scaleChanged REVISION(2, 14))
Q_CLASSINFO("DefaultProperty", "pathElements")
QML_NAMED_ELEMENT(Path)
@ -517,10 +518,14 @@ public:
QSizeF scale() const;
void setScale(const QSizeF &scale);
bool simplified() const;
void setSimplified(bool simplified);
Q_SIGNALS:
void changed();
void startXChanged();
void startYChanged();
Q_REVISION(6, 6) void simplifiedChanged();
Q_REVISION(2, 14) void scaleChanged();
protected:

View File

@ -53,6 +53,7 @@ public:
bool closed = false;
bool componentComplete = true;
bool isShapePath = false;
bool simplified = false;
};
QT_END_NAMESPACE

View File

@ -20,12 +20,16 @@ qt_internal_add_qml_module(QuickShapesPrivate
qquickshape_p_p.h
qquickshapegenericrenderer.cpp qquickshapegenericrenderer_p.h
qquickshapesglobal.h qquickshapesglobal_p.h
qquickshapecurverenderer.cpp qquickshapecurverenderer_p.h qquickshapecurverenderer_p_p.h
qt_delaunay_triangulator.cpp
qt_quadratic_bezier.cpp
qquickshapesoftwarerenderer.cpp qquickshapesoftwarerenderer_p.h
PUBLIC_LIBRARIES
Qt::Core
Qt::GuiPrivate
Qt::Qml
Qt::QuickPrivate
Qt::ShaderTools
GENERATE_CPP_EXPORTS
GENERATE_PRIVATE_CPP_EXPORTS
)
@ -51,4 +55,120 @@ qt_internal_add_shaders(QuickShapesPrivate "qtquickshapes_shaders"
"shaders_ng/radialgradient.frag"
"shaders_ng/conicalgradient.vert"
"shaders_ng/conicalgradient.frag"
"shaders_ng/shapecurve.frag"
"shaders_ng/shapecurve.vert"
"shaders_ng/wireframe.frag"
"shaders_ng/wireframe.vert"
)
qt_internal_add_shaders(QuickShapesPrivate "shaders_stroke"
BATCHABLE
PRECOMPILE
OPTIMIZED
DEFINES "STROKE"
PREFIX
"/qt-project.org/shapes"
FILES
"shaders_ng/shapecurve.frag"
"shaders_ng/shapecurve.vert"
OUTPUTS
"shaders_ng/shapecurve_stroke.frag.qsb"
"shaders_ng/shapecurve_stroke.vert.qsb"
)
qt_internal_add_shaders(QuickShapesPrivate "shaders_lg"
BATCHABLE
PRECOMPILE
OPTIMIZED
DEFINES "LINEARGRADIENT"
PREFIX
"/qt-project.org/shapes"
FILES
"shaders_ng/shapecurve.frag"
"shaders_ng/shapecurve.vert"
OUTPUTS
"shaders_ng/shapecurve_lg.frag.qsb"
"shaders_ng/shapecurve_lg.vert.qsb"
)
qt_internal_add_shaders(QuickShapesPrivate "shaders_lg_stroke"
BATCHABLE
PRECOMPILE
OPTIMIZED
DEFINES
"LINEARGRADIENT"
"STROKE"
PREFIX
"/qt-project.org/shapes"
FILES
"shaders_ng/shapecurve.frag"
"shaders_ng/shapecurve.vert"
OUTPUTS
"shaders_ng/shapecurve_lg_stroke.frag.qsb"
"shaders_ng/shapecurve_lg_stroke.vert.qsb"
)
qt_internal_add_shaders(QuickShapesPrivate "shaders_rg"
BATCHABLE
PRECOMPILE
OPTIMIZED
DEFINES "RADIALGRADIENT"
PREFIX
"/qt-project.org/shapes"
FILES
"shaders_ng/shapecurve.frag"
"shaders_ng/shapecurve.vert"
OUTPUTS
"shaders_ng/shapecurve_rg.frag.qsb"
"shaders_ng/shapecurve_rg.vert.qsb"
)
qt_internal_add_shaders(QuickShapesPrivate "shaders_rg_stroke"
BATCHABLE
PRECOMPILE
OPTIMIZED
DEFINES
"RADIALGRADIENT"
"STROKE"
PREFIX
"/qt-project.org/shapes"
FILES
"shaders_ng/shapecurve.frag"
"shaders_ng/shapecurve.vert"
OUTPUTS
"shaders_ng/shapecurve_rg_stroke.frag.qsb"
"shaders_ng/shapecurve_rg_stroke.vert.qsb"
)
qt_internal_add_shaders(QuickShapesPrivate "shaders_cg"
BATCHABLE
PRECOMPILE
OPTIMIZED
DEFINES
"CONICALGRADIENT"
PREFIX
"/qt-project.org/shapes"
FILES
"shaders_ng/shapecurve.frag"
"shaders_ng/shapecurve.vert"
OUTPUTS
"shaders_ng/shapecurve_cg.frag.qsb"
"shaders_ng/shapecurve_cg.vert.qsb"
)
qt_internal_add_shaders(QuickShapesPrivate "shaders_cg_stroke"
BATCHABLE
PRECOMPILE
OPTIMIZED
DEFINES
"CONICALGRADIENT"
"STROKE"
PREFIX
"/qt-project.org/shapes"
FILES
"shaders_ng/shapecurve.frag"
"shaders_ng/shapecurve.vert"
OUTPUTS
"shaders_ng/shapecurve_cg_stroke.frag.qsb"
"shaders_ng/shapecurve_cg_stroke.vert.qsb"
)

View File

@ -5,6 +5,7 @@
#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>
@ -639,6 +640,7 @@ void QQuickShapePrivate::_q_shapePathChanged()
Q_Q(QQuickShape);
spChanged = true;
q->polish();
emit q->boundingRectChanged();
}
void QQuickShapePrivate::setStatus(QQuickShape::Status newStatus)
@ -678,6 +680,45 @@ QQuickShape::~QQuickShape()
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
Added as technology preview in Qt 6.6.
Experimental renderer which triangulates the polygonal internal hull of the shape,
similar to \c Shape.GeometryRenderer. But instead of also triangulating curved areas,
this renderer renders curved areas using a specialized fragment shader. This means that
the shape does not need to be re-tesselated when it changes size or is zoomed. For
supported shapes, this can give improved runtime performance in cases where the shapes
are repeatedly transformed.
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 can be optionally selected using the \l preferredRendererType property.
In addition to rendering smooth curves regardless of zoom level, this renderer applies
anti-aliasing without enabling MSAA on the surface, which may provide performance gain.
Note that \c Shape.CurveRenderer is currently regarded as experimental and has several
limitations:
\list 1
\li The \c GL_OES_standard_derivatives extension to OpenGL is required when the OpenGL
RHI backend is in use (this is available by default on OpenGL ES 3 and later, but
optional in OpenGL ES 2).
\li Only quadratic curves are supported (cubic curves will be approximated by quadratic
curves).
\li Shapes where elements intersect are not supported. Use the \l Path.simplified
property to remove self-intersections from such shapes.
\li Shapes that span a large numerical range, such as a long string of text, may have
issues. Consider splitting these shapes into multiple ones, for instance by making
a \l PathText for each individual word.
Due to the fact that the \c Shape.CurveRenderer approximates cubic curves, there are certain
shapes it will not render accurately. For instance, circular arcs are not representable using quadratic
curves and will only look approximately correct. If the visual representation is
insufficient for a particular shape, consider using \c Shape.GeometryRenderer instead.
\note The \c Shape.CurveRenderer is currently considered a tech preview, thus the name of
this enum may change in future versions of Qt and some shapes may render incorrectly.
*/
QQuickShape::RendererType QQuickShape::rendererType() const
@ -686,6 +727,56 @@ QQuickShape::RendererType QQuickShape::rendererType() const
return d->rendererType;
}
/*!
\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 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.
\l 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.
\note This API is considered tech preview and may change or be removed in future versions of
Qt.
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
@ -719,6 +810,23 @@ void QQuickShape::setAsynchronous(bool async)
}
}
/*!
\qmlproperty rect QtQuick.Shapes::Shape::boundingRect
\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) {
brect = brect.united(path->path().boundingRect());
}
return brect;
}
/*!
\qmlproperty bool QtQuick.Shapes::Shape::vendorExtensionsEnabled
@ -902,6 +1010,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)
@ -936,36 +1050,73 @@ 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();
}
return node;
}
QQuickShape::RendererType QQuickShapePrivate::selectRendererType()
{
QQuickShape::RendererType res = QQuickShape::UnknownRenderer;
Q_Q(QQuickShape);
QSGRendererInterface *ri = q->window()->rendererInterface();
if (!ri)
return res;
static const bool environmentPreferCurve =
qEnvironmentVariable("QT_QUICKSHAPES_BACKEND").toLower() == QLatin1String("curve");
switch (ri->graphicsApi()) {
case QSGRendererInterface::Software:
res = QQuickShape::SoftwareRenderer;
break;
default:
if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) {
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);
QSGRendererInterface *ri = q->window()->rendererInterface();
if (!ri)
QQuickShape::RendererType selectedType = selectRendererType();
if (selectedType == QQuickShape::UnknownRenderer)
return;
switch (ri->graphicsApi()) {
case QSGRendererInterface::Software:
rendererType = QQuickShape::SoftwareRenderer;
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:
if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) {
rendererType = QQuickShape::GeometryRenderer;
renderer = new QQuickShapeGenericRenderer(q);
} else {
qWarning("No path backend for this graphics API yet");
}
Q_UNREACHABLE();
break;
}
}
@ -989,9 +1140,15 @@ QSGNode *QQuickShapePrivate::createNode()
break;
default:
if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) {
node = new QQuickShapeGenericNode;
static_cast<QQuickShapeGenericRenderer *>(renderer)->setRootNode(
static_cast<QQuickShapeGenericNode *>(node));
if (rendererType == QQuickShape::CurveRenderer) {
node = new QQuickShapeCurveNode;
static_cast<QQuickShapeCurveRenderer *>(renderer)->setRootNode(
static_cast<QQuickShapeCurveNode *>(node));
} else {
node = new QQuickShapeGenericNode;
static_cast<QQuickShapeGenericRenderer *>(renderer)->setRootNode(
static_cast<QQuickShapeGenericNode *>(node));
}
} else {
qWarning("No path backend for this graphics API yet");
}

View File

@ -288,8 +288,12 @@ class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShape : public QQuickItem
Q_PROPERTY(RendererType rendererType READ rendererType NOTIFY rendererChanged)
Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged)
Q_PROPERTY(bool vendorExtensionsEnabled READ vendorExtensionsEnabled WRITE setVendorExtensionsEnabled NOTIFY vendorExtensionsEnabledChanged)
Q_PROPERTY(RendererType preferredRendererType READ preferredRendererType
WRITE setPreferredRendererType NOTIFY preferredRendererTypeChanged REVISION(6, 6))
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
Q_PROPERTY(ContainsMode containsMode READ containsMode WRITE setContainsMode NOTIFY containsModeChanged REVISION(1, 11))
Q_PROPERTY(QRectF boundingRect READ boundingRect NOTIFY boundingRectChanged REVISION(6, 6))
Q_PROPERTY(QQmlListProperty<QObject> data READ data)
Q_CLASSINFO("DefaultProperty", "data")
QML_NAMED_ELEMENT(Shape)
@ -300,7 +304,8 @@ public:
UnknownRenderer,
GeometryRenderer,
NvprRenderer,
SoftwareRenderer
SoftwareRenderer,
CurveRenderer
};
Q_ENUM(RendererType)
@ -325,6 +330,11 @@ public:
bool asynchronous() const;
void setAsynchronous(bool async);
Q_REVISION(6, 6) RendererType preferredRendererType() const;
Q_REVISION(6, 6) void setPreferredRendererType(RendererType preferredType);
Q_REVISION(6, 6) QRectF boundingRect() const override;
bool vendorExtensionsEnabled() const;
void setVendorExtensionsEnabled(bool enable);
@ -349,6 +359,8 @@ Q_SIGNALS:
void asynchronousChanged();
void vendorExtensionsEnabledChanged();
void statusChanged();
Q_REVISION(6, 6) void preferredRendererTypeChanged();
Q_REVISION(6, 6) void boundingRectChanged();
Q_REVISION(1, 11) void containsModeChanged();
private:

View File

@ -41,7 +41,7 @@ public:
enum FillGradientType { NoGradient = 0, LinearGradient, RadialGradient, ConicalGradient };
struct GradientDesc { // can fully describe a linear/radial/conical gradient
QGradientStops stops;
QQuickShapeGradient::SpreadMode spread;
QQuickShapeGradient::SpreadMode spread = QQuickShapeGradient::PadSpread;
QPointF a; // start (L) or center point (R/C)
QPointF b; // end (L) or focal point (R)
qreal v0; // center radius (R) or start angle (C)
@ -127,6 +127,7 @@ public:
QQuickShapePrivate();
~QQuickShapePrivate();
QQuickShape::RendererType selectRendererType();
void createRenderer();
QSGNode *createNode();
void sync();
@ -146,8 +147,10 @@ public:
int syncTimeCounter = 0;
QQuickShape::Status status = QQuickShape::Null;
QQuickShape::RendererType rendererType = QQuickShape::UnknownRenderer;
QQuickShape::RendererType preferredType = QQuickShape::UnknownRenderer;
QQuickShape::ContainsMode containsMode = QQuickShape::BoundingRectContains;
bool spChanged = false;
bool rendererChanged = false;
bool async = false;
bool enableVendorExts = false;
bool syncTimingActive = false;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,345 @@
// Copyright (C) 2022 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
#ifndef QQUICKSHAPECURVERENDERER_P_H
#define QQUICKSHAPECURVERENDERER_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 <QtQuickShapes/private/qquickshapesglobal_p.h>
#include <QtQuickShapes/private/qquickshape_p_p.h>
#include <qsgnode.h>
#include <qsggeometry.h>
#include <qsgmaterial.h>
#include <qsgrendererinterface.h>
#include <qsgtexture.h>
#include <QtCore/qrunnable.h>
#include <QtGui/private/qtriangulator_p.h>
QT_BEGIN_NAMESPACE
class QuadPath
{
public:
void moveTo(const QVector2D &to)
{
subPathToStart = true;
currentPoint = to;
}
void lineTo(const QVector2D &to)
{
addElement({}, to, true);
}
void quadTo(const QVector2D &control, const QVector2D &to)
{
addElement(control, to);
}
QRectF controlPointRect() const;
Qt::FillRule fillRule() const { return m_fillRule; }
void setFillRule(Qt::FillRule rule) { m_fillRule = rule; }
void reserve(qsizetype size) { m_elements.reserve(size); }
qsizetype elementCount() const { return m_elements.size(); }
qsizetype elementCountRecursive() const;
static QuadPath fromPainterPath(const QPainterPath &path);
QPainterPath toPainterPath() const;
QuadPath subPathsClosed() const;
class Element
{
public:
Element ()
: m_isSubpathStart(false), m_isSubpathEnd(false), m_isLine(false)
{
}
bool isSubpathStart() const
{
return m_isSubpathStart;
}
bool isSubpathEnd() const
{
return m_isSubpathEnd;
}
bool isLine() const
{
return m_isLine;
}
bool isConvex() const
{
return m_curvatureFlags & Convex;
}
QVector2D startPoint() const
{
return sp;
}
QVector2D controlPoint() const
{
return cp;
}
QVector2D endPoint() const
{
return ep;
}
QVector2D midPoint() const
{
return isLine() ? 0.5f * (sp + ep) : (0.25f * sp) + (0.5f * cp) + (0.25 * ep);
}
QVector3D uvForPoint(QVector2D p) const;
qsizetype childCount() const { return m_numChildren; }
qsizetype indexOfChild(qsizetype childNumber) const
{
Q_ASSERT(childNumber >= 0 && childNumber < childCount());
return -(m_firstChildIndex + 1 + childNumber);
}
QVector2D pointAtFraction(float t) const;
QVector2D tangentAtFraction(float t) const
{
return isLine() ? (ep - sp) : ((1 - t) * 2 * (cp - sp)) + (t * 2 * (ep - cp));
}
QVector2D normalAtFraction(float t) const
{
const QVector2D tan = tangentAtFraction(t);
return QVector2D(-tan.y(), tan.x());
}
private:
int intersectionsAtY(float y, float *fractions) const;
enum CurvatureFlags : quint8 {
CurvatureUndetermined = 0,
FillOnRight = 1,
Convex = 2
};
QVector2D sp;
QVector2D cp;
QVector2D ep;
int m_firstChildIndex = 0;
quint8 m_numChildren = 0;
CurvatureFlags m_curvatureFlags = CurvatureUndetermined;
quint8 m_isSubpathStart : 1;
quint8 m_isSubpathEnd : 1;
quint8 m_isLine : 1;
friend class QuadPath;
friend QDebug operator<<(QDebug, const QuadPath::Element &);
};
template<typename Func>
void iterateChildrenOf(Element &e, Func &&lambda)
{
const qsizetype lastChildIndex = e.m_firstChildIndex + e.childCount() - 1;
for (qsizetype i = e.m_firstChildIndex; i <= lastChildIndex; i++) {
Element &c = m_childElements[i];
if (c.childCount() > 0)
iterateChildrenOf(c, lambda);
else
lambda(c);
}
}
template<typename Func>
void iterateChildrenOf(const Element &e, Func &&lambda) const
{
const qsizetype lastChildIndex = e.m_firstChildIndex + e.childCount() - 1;
for (qsizetype i = e.m_firstChildIndex; i <= lastChildIndex; i++) {
const Element &c = m_childElements[i];
if (c.childCount() > 0)
iterateChildrenOf(c, lambda);
else
lambda(c);
}
}
template<typename Func>
void iterateElements(Func &&lambda)
{
for (auto &e : m_elements) {
if (e.childCount() > 0)
iterateChildrenOf(e, lambda);
else
lambda(e);
}
}
template<typename Func>
void iterateElements(Func &&lambda) const
{
for (auto &e : m_elements) {
if (e.childCount() > 0)
iterateChildrenOf(e, lambda);
else
lambda(e);
}
}
void splitElementAt(qsizetype index);
Element &elementAt(qsizetype i) { return i < 0 ? m_childElements[-(i + 1)] : m_elements[i]; }
const Element &elementAt(qsizetype i) const
{
return i < 0 ? m_childElements[-(i + 1)] : m_elements[i];
}
qsizetype indexOfChildAt(qsizetype i, qsizetype childNumber) const
{
return elementAt(i).indexOfChild(childNumber);
}
void addCurvatureData();
bool contains(const QVector2D &v) const;
private:
void addElement(const QVector2D &control, const QVector2D &to, bool isLine = false);
Element::CurvatureFlags coordinateOrderOfElement(const Element &element) const;
static bool isControlPointOnLeft(const Element &element);
static QVector2D closestPointOnLine(const QVector2D &start,
const QVector2D &end,
const QVector2D &p);
static bool isPointOnLeft(const QVector2D &p, const QVector2D &sp, const QVector2D &ep);
static bool isPointOnLine(const QVector2D &p, const QVector2D &sp, const QVector2D &ep);
static bool isPointNearLine(const QVector2D &p, const QVector2D &sp, const QVector2D &ep);
friend QDebug operator<<(QDebug, const QuadPath &);
bool subPathToStart = true;
Qt::FillRule m_fillRule = Qt::OddEvenFill;
QVector2D currentPoint;
QList<Element> m_elements;
QList<Element> m_childElements;
};
QDebug operator<<(QDebug, const QuadPath::Element &);
QDebug operator<<(QDebug, const QuadPath &);
class QQuickShapeCurveNode : public QSGNode
{
public:
QQuickShapeCurveNode();
};
class QQuickShapeCurveRenderer : public QQuickAbstractPathRenderer
{
public:
QQuickShapeCurveRenderer(QQuickItem *)
: m_rootNode(nullptr)
{ }
~QQuickShapeCurveRenderer() override;
void beginSync(int totalCount, bool *countChanged) 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, 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<qreal> &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 Flags{}; }
void updateNode() override;
void setRootNode(QQuickShapeCurveNode *node);
using NodeList = QVector<QSGGeometryNode *>;
enum DirtyFlag
{
GeometryDirty = 1,
UniformsDirty = 2
};
//### PathData used to be private, but I am using it in a static function. TODO: FIX ###
struct PathData {
bool isFillVisible() const { return fillColor.alpha() > 0 || gradientType != NoGradient; }
bool isStrokeVisible() const
{
return validPenWidth && pen.color().alpha() > 0 && pen.style() != Qt::NoPen;
}
bool useFragmentShaderStroker() const;
FillGradientType gradientType = NoGradient;
GradientDesc gradient;
QPainterPath fillPath;
QPainterPath originalPath;
QuadPath path;
QColor fillColor;
Qt::FillRule fillRule = Qt::OddEvenFill;
QPen pen;
int m_dirty = 0;
bool validPenWidth = true;
bool convexConcaveResolved = false;
NodeList fillNodes;
NodeList strokeNodes;
NodeList debugNodes;
};
enum DebugVisualizationOption {
NoDebug = 0,
DebugCurves = 0x01,
DebugWireframe = 0x02
};
Q_QUICKSHAPES_PRIVATE_EXPORT static int debugVisualization();
Q_QUICKSHAPES_PRIVATE_EXPORT static void setDebugVisualization(int options);
private:
void deleteAndClear(NodeList *nodeList);
QVector<QSGGeometryNode *> addPathNodesBasic(const PathData &pathData, NodeList *debugNodes);
QVector<QSGGeometryNode *> addPathNodesDelauneyTest(const PathData &pathData, NodeList *debugNodes);
QSGGeometryNode *addLoopBlinnNodes(const QTriangleSet &triangles,
const QVarLengthArray<quint32> &extraIndices,
int startConcaveCurves,
const PathData &pathData,
NodeList *debugNodes);
void solveOverlaps(QuadPath &path);
QQuickShapeCurveNode *m_rootNode;
QVector<PathData> m_paths;
static int debugVisualizationFlags;
};
QT_END_NAMESPACE
#endif // QQUICKSHAPECURVERENDERER_P_H

View File

@ -0,0 +1,77 @@
// Copyright (C) 2023 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
#ifndef QQUICKSHAPECURVERENDERER_P_P_H
#define QQUICKSHAPECURVERENDERER_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 <QtQuickShapes/private/qquickshapesglobal_p.h>
#include <QtGui/qvector2d.h>
#include <QPainterPath>
#include <QLoggingCategory>
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcShapeCurveRenderer);
struct QtPathVertex
{
QVector2D point;
int id;
quint32 binX = 0;
quint32 binY = 0;
};
struct QtPathEdge
{
quint32 startIndex;
quint32 endIndex;
int id;
};
struct QtPathTriangle
{
QtPathTriangle(quint32 i1, quint32 i2, quint32 i3, int d) : v1Index(i1), v2Index(i2), v3Index(i3), id(d) {}
quint32 v1Index;
quint32 v2Index;
quint32 v3Index;
quint32 adjacentTriangle1 = quint32(-1); // Adjacent to v1-v2
quint32 adjacentTriangle2 = quint32(-1); // Adjacent to v2-v3
quint32 adjacentTriangle3 = quint32(-1); // Adjacent to v3-v1
// Used by triangulator
quint32 lastSeenVertex = quint32(-1);
// Should this triangle be rendered? Set to false for triangles connecting to super-triangle
bool isValid = true;
int id;
};
constexpr bool operator==(const QtPathTriangle& lhs, const QtPathTriangle& rhs)
{
return lhs.id == rhs.id
&& lhs.v1Index == rhs.v1Index
&& lhs.v2Index == rhs.v2Index
&& lhs.v3Index == rhs.v3Index;
}
class QBezier;
Q_QUICKSHAPES_PRIVATE_EXPORT QPolygonF qt_toQuadratics(const QBezier &b, qreal errorLimit = 0.2);
Q_QUICKSHAPES_PRIVATE_EXPORT QList<QtPathTriangle> qtDelaunayTriangulator(const QList<QtPathVertex> &vertices, const QList<QtPathEdge> &edges, const QPainterPath &path);
QT_END_NAMESPACE
#endif //QQUICKSHAPECURVERENDERER_P_P_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,139 @@
// Copyright (C) 2023 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
// ---------------- Cubic -> Quadratic path stuff - temporarily here -----------
#include <private/qbezier_p.h>
#include <QtMath>
QT_BEGIN_NAMESPACE
#if 0
static bool qt_isQuadratic(const QBezier &b)
{
const qreal f = 3.0 / 2.0;
const QPointF c1 = b.pt1() + f * (b.pt2() - b.pt1());
const QPointF c2 = b.pt4() + f * (b.pt3() - b.pt4());
return c1 == c2;
}
#endif
static qreal qt_scoreQuadratic(const QBezier &b, QPointF qcp)
{
// Construct a cubic from the quadratic, and compare its control points to the originals'
const QRectF bounds = b.bounds();
qreal dim = QLineF(bounds.topLeft(), bounds.bottomRight()).length();
if (qFuzzyIsNull(dim))
return 0;
const qreal f = 2.0 / 3;
const QPointF cp1 = b.pt1() + f * (qcp - b.pt1());
const QPointF cp2 = b.pt4() + f * (qcp - b.pt4());
const QLineF d1(b.pt2(), cp1);
const QLineF d2(b.pt3(), cp2);
return qMax(d1.length(), d2.length()) / dim;
}
static qreal qt_quadraticForCubic(const QBezier &b, QPointF *qcp)
{
const QLineF st = b.startTangent();
const QLineF et = b.endTangent();
if (st.intersects(et, qcp) == QLineF::NoIntersection)
*qcp = b.midPoint();
return qt_scoreQuadratic(b, *qcp);
}
static int qt_getInflectionPoints(const QBezier &orig, qreal *tpoints)
{
auto isValidRoot = [](qreal r) {
return qIsFinite(r) && (r > 0) && (!qFuzzyIsNull(float(r))) && (r < 1)
&& (!qFuzzyIsNull(float(r - 1)));
};
// normalize so pt1.x,pt1.y,pt4.y == 0
QTransform xf;
const QLineF l(orig.pt1(), orig.pt4());
xf.rotate(l.angle());
xf.translate(-orig.pt1().x(), -orig.pt1().y());
const QBezier n = orig.mapBy(xf);
Q_ASSERT(n.pt1() == QPoint() && qFuzzyIsNull(float(n.pt4().y())));
const qreal p = n.pt3().x() * n.pt2().y();
const qreal q = n.pt4().x() * n.pt2().y();
const qreal r = n.pt2().x() * n.pt3().y();
const qreal s = n.pt4().x() * n.pt3().y();
const qreal a = 36 * ((-3 * p) + (2 * q) + (3 * r) - s);
if (!a)
return 0;
const qreal b = -18 * (((3 * p) - q) - (3 * r));
const qreal c = 18 * (r - p);
const qreal rad = (b * b) - (2 * a * c);
if (rad < 0)
return 0;
const qreal sqr = qSqrt(rad);
const qreal root1 = (b + sqr) / a;
const qreal root2 = (b - sqr) / a;
int res = 0;
if (isValidRoot(root1))
tpoints[res++] = root1;
if (root2 != root1 && isValidRoot(root2))
tpoints[res++] = root2;
if (res == 2 && tpoints[0] > tpoints[1])
qSwap(tpoints[0], tpoints[1]);
return res;
}
static void qt_addToQuadratics(const QBezier &b, QPolygonF *p, qreal spanlength, qreal errorLimit)
{
Q_ASSERT((spanlength > 0) && !(spanlength > 1));
QPointF qcp;
bool isOk = (qt_quadraticForCubic(b, &qcp) < errorLimit); // error limit, careful
if (isOk || spanlength < 0.1) {
p->append(qcp);
p->append(b.pt4());
} else {
QBezier rhs = b;
QBezier lhs;
rhs.parameterSplitLeft(0.5, &lhs);
qt_addToQuadratics(lhs, p, spanlength / 2, errorLimit);
qt_addToQuadratics(rhs, p, spanlength / 2, errorLimit);
}
}
QPolygonF qt_toQuadratics(const QBezier &b, qreal errorLimit)
{
QPolygonF res;
res.reserve(16);
res.append(b.pt1());
const QRectF cpr = b.bounds();
qreal epsilon = QLineF(cpr.topLeft(), cpr.bottomRight()).length() * 0.5 * errorLimit;
bool degenerate = false;
if (QLineF(b.pt2(), b.pt1()).length() < epsilon) {
res.append(b.pt3());
degenerate = true;
} else if (QLineF(b.pt4(), b.pt3()).length() < epsilon) {
res.append(b.pt2());
degenerate = true;
}
if (degenerate) {
res.append(b.pt4());
return res;
}
qreal infPoints[2];
int numInfPoints = qt_getInflectionPoints(b, infPoints);
qreal t0 = 0;
for (int i = 0; i < numInfPoints + 1; i++) { // #segments == #inflectionpoints + 1
qreal t1 = (i < numInfPoints) ? infPoints[i] : 1;
QBezier segment = b.bezierOnInterval(t0, t1);
qt_addToQuadratics(segment, &res, t1 - t0, errorLimit);
t0 = t1;
}
return res;
}
QT_END_NAMESPACE

View File

@ -0,0 +1,108 @@
#version 440
layout(location = 0) in vec3 qt_TexCoord;
layout(location = 1) in vec4 debugColor;
#if defined(LINEARGRADIENT)
layout(location = 2) in float gradTabIndex;
#elif defined(RADIALGRADIENT) || defined(CONICALGRADIENT)
layout(location = 2) in vec2 coord;
#endif
layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
#if defined(STROKE)
vec4 strokeColor;
float strokeWidth;
float reserved1;
float reserved2;
float reserved3;
#endif
#if defined(LINEARGRADIENT)
vec2 gradientStart;
vec2 gradientEnd;
#elif defined(RADIALGRADIENT)
vec2 translationPoint;
vec2 focalToCenter;
float centerRadius;
float focalRadius;
#elif defined(CONICALGRADIENT)
vec2 translationPoint;
float angle;
#else
vec4 color;
#endif
} ubuf;
#define INVERSE_2PI 0.1591549430918953358
#if defined(LINEARGRADIENT) || defined(RADIALGRADIENT) || defined(CONICALGRADIENT)
layout(binding = 1) uniform sampler2D gradTabTexture;
#endif
vec4 baseColor()
{
#if defined(LINEARGRADIENT)
return texture(gradTabTexture, vec2(gradTabIndex, 0.5));
#elif defined(RADIALGRADIENT)
float rd = ubuf.centerRadius - ubuf.focalRadius;
float b = 2.0 * (rd * ubuf.focalRadius + dot(coord, ubuf.focalToCenter));
float fmp2_m_radius2 = -ubuf.focalToCenter.x * ubuf.focalToCenter.x - ubuf.focalToCenter.y * ubuf.focalToCenter.y + rd * rd;
float inverse_2_fmp2_m_radius2 = 1.0 / (2.0 * fmp2_m_radius2);
float det = b * b - 4.0 * fmp2_m_radius2 * ((ubuf.focalRadius * ubuf.focalRadius) - dot(coord, coord));
vec4 result = vec4(0.0);
if (det >= 0.0) {
float detSqrt = sqrt(det);
float w = max((-b - detSqrt) * inverse_2_fmp2_m_radius2, (-b + detSqrt) * inverse_2_fmp2_m_radius2);
if (ubuf.focalRadius + w * (ubuf.centerRadius - ubuf.focalRadius) >= 0.0)
result = texture(gradTabTexture, vec2(w, 0.5));
}
return result;
#elif defined(CONICALGRADIENT)
float t;
if (abs(coord.y) == abs(coord.x))
t = (atan(-coord.y + 0.002, coord.x) + ubuf.angle) * INVERSE_2PI;
else
t = (atan(-coord.y, coord.x) + ubuf.angle) * INVERSE_2PI;
return texture(gradTabTexture, vec2(t - floor(t), 0.5));
#else
return vec4(ubuf.color.rgb, 1.0) * ubuf.color.a;
#endif
}
void main()
{
float f = qt_TexCoord.z * (qt_TexCoord.x * qt_TexCoord.x - qt_TexCoord.y) // curve
+ (1.0 - abs(qt_TexCoord.z)) * (qt_TexCoord.x - qt_TexCoord.y); // line
#if defined(STROKE)
float _ddx = dFdx(f);
float _ddy = dFdy(f);
float df = length(vec2(_ddx, _ddy));
float distance = (f / df); // distance from centre of fragment to line
float halfStrokeWidth = ubuf.strokeWidth / 2.0;
// calculate stroke
float strokeCoverage = 1.0 - clamp(0.5 + abs(distance) - halfStrokeWidth, 0.0, 1.0);
vec4 stroke = ubuf.strokeColor * strokeCoverage;
float fillCoverage = clamp(0.5 + f / df, 0.0, 1.0);
vec4 fill = baseColor() * fillCoverage;
vec4 combined = fill * (1.0 - stroke.a) + stroke * stroke.a;
// finally mix in debug
fragColor = mix(combined, vec4(debugColor.rgb, 1.0), debugColor.a);
// TODO: support both outline and fill
#else
float df = fwidth(f);
fragColor = mix(baseColor() * clamp(0.5 + f / df, 0.0, 1.0), vec4(debugColor.rgba), debugColor.a);
#endif
}

View File

@ -0,0 +1,58 @@
#version 440
layout(location = 0) in vec4 vertexCoord;
layout(location = 1) in vec3 vertexTexCoord;
layout(location = 2) in vec4 vertexDebugColor;
layout(location = 0) out vec3 qt_TexCoord;
layout(location = 1) out vec4 debugColor;
#if defined(LINEARGRADIENT)
layout(location = 2) out float gradTabIndex;
#elif defined(RADIALGRADIENT) || defined(CONICALGRADIENT)
layout(location = 2) out vec2 coord;
#endif
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
#if defined(STROKE)
vec4 strokeColor;
float strokeWidth;
float reserved1;
float reserved2;
float reserved3;
#endif
#if defined(LINEARGRADIENT)
vec2 gradientStart;
vec2 gradientEnd;
#elif defined(RADIALGRADIENT)
vec2 translationPoint;
vec2 focalToCenter;
float centerRadius;
float focalRadius;
#elif defined(CONICALGRADIENT)
vec2 translationPoint;
float angle;
#else
vec4 color;
#endif
} ubuf;
out gl_PerVertex { vec4 gl_Position; };
void main()
{
qt_TexCoord = vertexTexCoord;
debugColor = vertexDebugColor;
#if defined(LINEARGRADIENT)
vec2 gradVec = ubuf.gradientEnd - ubuf.gradientStart;
gradTabIndex = dot(gradVec, vertexCoord.xy - ubuf.gradientStart.xy) / dot(gradVec, gradVec);
#elif defined(RADIALGRADIENT) || defined(CONICALGRADIENT)
coord = vertexCoord.xy - ubuf.translationPoint;
#endif
gl_Position = ubuf.qt_Matrix * vertexCoord;
}

View File

@ -0,0 +1,18 @@
#version 440
layout(location = 0) out vec4 fragColor;
layout(location = 0) in vec3 barycentric;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
} ubuf;
void main()
{
float f = min(barycentric.x, min(barycentric.y, barycentric.z));
float d = fwidth(f * 1.5);
float alpha = smoothstep(0.0, d, f);
//alpha = 1.0 - step(0.5, barycentric.x);
fragColor = vec4(1.0, 0.2, 1.0, 1.0) * (1.0 - alpha);
}

View File

@ -0,0 +1,17 @@
#version 440
layout(location = 0) in vec4 vertexCoord;
layout(location = 1) in vec3 vertexBarycentric;
layout(location = 0) out vec3 barycentric;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
} ubuf;
out gl_PerVertex { vec4 gl_Position; };
void main()
{
barycentric = vertexBarycentric;
gl_Position = ubuf.qt_Matrix * vertexCoord;
}

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0" y="0" width="498" height="450" viewBox="0, 0, 498, 450">
<g id="Layer_1">
<path d="M377.478,156.433 C390.511,164.689 405.743,177.699 401.901,188.702 L400.821,214.061 L387.401,200.274 C377.97,203.879 355.363,209.091 344.882,209.309 C327.622,209.086 318.23,200.447 301.246,210.607 L300.15,211.261 C287.503,219.529 278.841,222.432 261.924,220.372 C240.649,217.635 222.088,219.976 200.944,221.401 C189.111,222.851 186.176,229.895 185.117,238.294 C184.679,248.402 186.989,251.163 188.144,259.871 C189.694,271.542 180.447,281.398 175.785,292.36 C171.836,302.716 170.28,317.969 170.783,332.682 L350.629,302.414 L497.063,442.618 L439.68,448.335 L171.109,338.724 C171.796,348.056 173.32,356.85 175.59,363.646 C180.514,378.376 199.634,392.772 200.249,404.625 C200.896,417.212 187.414,419.239 174.601,416.362 C155.05,411.713 154.299,415.621 141.357,426.569 C113.335,450.279 98.118,429.304 71.605,431.841 C55.253,433.405 40.729,432.918 27.507,424.542 C16.53,416.991 17.367,406.636 15.482,396.464 C14.067,388.824 15.899,382.701 25.702,379.28 C39.023,376.32 56.656,378.437 65.04,370.918 C74.247,362.664 72.98,362.849 84.625,355.865 C105.108,343.576 98.591,331.952 98.436,313.869 C98.388,308.464 98.797,303.473 99.42,298.096 C101.462,281.407 101.799,265.05 101.034,248.311 C100.788,243.23 100.462,237.986 98.094,233.185 C86.734,214.876 67.777,210.151 74.41,184.825 C83.029,151.909 117.925,169.13 136.27,162.996 C147.594,159.208 152.842,157.385 165.027,154.781 C169.689,153.785 174.381,152.847 179.057,151.883 C191.796,149.238 202.615,147.549 215.653,146.058 C237.815,143.521 239.756,129.349 272.799,126.685 C294.042,124.898 292.304,121.222 309.264,115.415 C330.998,107.974 345.932,115.338 355.323,130.552 C363.442,143.706 362.125,146.461 377.478,156.433" fill="#D0CFCA"/>
<path d="M439.68,210.512 L439.68,448.335 L0.464,341.45 L0.464,103.627 L210.167,90.042 L439.68,210.512" fill="#DFB142"/>
<path d="M0.464,103.627 C53.025,50.996 164.147,14.658 292.833,14.465 L439.68,210.512 L0.464,103.627" fill="#F0C55C"/>
<path d="M439.68,272.823 L439.68,379.792 L0.464,272.907 L0.464,165.939 L439.68,272.823" fill="#685227"/>
<path d="M362.421,63.072 C375.451,73.881 390.685,90.917 386.843,105.325 L385.763,138.531 L372.343,120.479 C362.913,125.199 340.305,132.023 329.822,132.31 C312.565,132.018 303.174,120.703 286.188,134.008 L285.091,134.866 C272.443,145.693 263.784,149.494 246.864,146.795 C225.593,143.212 207.028,146.279 185.882,148.143 C174.055,150.042 171.114,159.266 170.059,170.264 C169.617,183.499 171.93,187.117 173.087,198.519 C174.633,213.801 165.388,226.707 160.726,241.06 C153.682,265.245 154.256,309.816 160.533,334.409 C165.455,353.698 184.577,372.548 185.187,388.07 C185.839,404.551 172.354,407.205 159.541,403.436 C139.991,397.352 139.242,402.465 126.298,416.803 C98.275,447.852 83.059,420.385 56.547,423.705 C40.193,425.756 25.669,425.118 12.448,414.151 C1.472,404.259 2.308,390.702 0.423,377.381 C-0.993,367.375 0.84,359.362 10.644,354.877 C23.964,351.004 41.596,353.777 49.98,343.931 C59.186,333.122 57.921,333.366 69.564,324.218 C90.05,308.127 83.529,292.907 83.374,269.229 C83.331,262.151 83.737,255.615 84.363,248.573 C86.4,226.719 86.737,205.301 85.978,183.381 C85.73,176.728 85.403,169.861 83.034,163.574 C71.676,139.599 52.721,133.411 59.351,100.248 C67.97,57.145 102.863,79.697 121.208,71.664 C132.535,66.704 137.782,64.318 149.968,60.907 C154.63,59.602 159.322,58.374 163.998,57.113 C176.736,53.648 187.556,51.437 200.594,49.483 C222.753,46.163 224.694,27.604 257.742,24.117 C278.982,21.776 277.247,16.962 294.207,9.359 C315.939,-0.384 330.873,9.258 340.265,29.18 C348.384,46.406 347.065,50.012 362.421,63.072" fill="#A02529"/>
<path d="M377.222,77.803 C384.346,86.596 389.209,96.457 386.843,105.325 L385.763,138.531 L372.343,120.479 C362.913,125.199 340.305,132.023 329.822,132.31 C312.565,132.018 303.174,120.703 286.188,134.008 L285.091,134.866 C272.443,145.693 263.784,149.494 246.864,146.795 C225.593,143.212 207.028,146.279 185.882,148.143 C174.055,150.042 171.114,159.266 170.059,170.264 C169.617,183.499 171.93,187.117 173.087,198.519 C174.633,213.801 165.388,226.707 160.726,241.06 C153.682,265.245 154.256,309.816 160.533,334.409 C165.455,353.698 184.577,372.548 185.187,388.07 C185.839,404.551 172.354,407.205 159.541,403.436 C139.991,397.352 139.242,402.465 126.298,416.803 C98.275,447.852 83.059,420.385 56.547,423.705 C41.406,425.604 27.835,425.198 15.41,416.418 C15.886,416.252 16.353,416.086 16.806,415.923 C40.174,407.529 48.443,396.154 76.787,401.466 C98.441,405.379 98.762,406.43 114.283,392.336 C122.29,385.065 130.835,382.674 136.828,376.579 C147.786,365.44 143.055,348.134 141.242,333.961 C139.79,322.607 138.545,311.693 137.868,300.247 C136.302,270.394 136.149,249.273 146.169,220.601 C156.699,185.664 142.878,187.253 136.676,158.625 C133.559,140.425 141.91,130.414 159.322,126.454 C174.809,123.09 185.026,122.958 200.438,117.255 C226.389,107.653 237.86,126.159 267.655,111.396 C290.299,100.173 288,101.231 313.813,100.553 C322.81,100.316 331.664,100.323 340.66,100.543 C354.877,100.939 372.706,99.879 376.406,83.305 C376.799,81.544 377.061,79.698 377.222,77.803" fill="#852124"/>
<path d="M116.994,372.295 C96.441,393.218 85.04,378.461 60.386,384.618 C41.425,389.804 40.515,394.084 19.935,387.145 C-2.438,379.129 8.007,369.233 23.439,362.447 C35.36,357.424 45.017,353.945 56.672,347.815 C66.414,342.692 74.814,340.533 84.187,336.313 C95.931,330.505 89.501,323.825 91.833,310.488 C97.182,279.909 97.906,262.688 96.68,231.982 C96.449,226.215 93.495,190.875 105.333,195.129 C112.567,197.728 113.875,226.189 114.663,233.57 C116.893,254.394 116.754,269.973 116.572,290.65 C116.391,311.867 117.946,313.248 125.413,333.1 C131.815,350.125 129.549,358.997 116.994,372.295 z M277.376,26.93 C256.4,34.431 249.969,33.482 233.64,49.962 C224.443,59.24 216.728,61.36 203.788,61.262 C187.182,60.92 176.702,55.995 160.816,63.608 C158.592,64.676 156.373,65.745 154.144,66.805 C141.264,72.87 114.971,74.232 107.809,85.082 C102.256,93.498 109.541,98.832 117.668,100.211 C132.249,102.681 153.315,101.858 167.441,97.815 C195.955,89.668 202.928,88.077 232.115,95.399 C252.075,98.504 255.43,86.181 270.652,77.257 C290.588,65.572 311.159,75.362 332.33,74.141 C343.4,73.555 354.434,70.833 345.116,57.437 C340.157,50.307 328.218,41.032 321.232,33.358 C305.834,16.439 297.153,19.871 277.376,26.93" fill="#BB242A"/>
<path d="M213.284,102.237 C235.308,102.237 253.268,93.724 253.268,83.285 C253.268,72.846 235.308,64.331 213.284,64.331 C191.261,64.331 173.301,72.846 173.301,83.285 C173.301,93.724 191.261,102.237 213.284,102.237" fill="#A02529"/>
<path d="M296.932,67.335 C315.226,67.335 330.146,60.262 330.146,51.591 C330.146,42.919 315.226,35.846 296.932,35.846 C278.638,35.846 263.715,42.919 263.715,51.591 C263.715,60.262 278.638,67.335 296.932,67.335" fill="#A02529"/>
<path d="M271.697,1.555 C274.782,1.555 277.761,2.011 280.573,2.854 C283.386,2.011 286.366,1.555 289.454,1.555 C306.489,1.555 320.297,15.363 320.297,32.398 C320.297,49.431 306.489,63.239 289.454,63.239 C286.366,63.239 283.386,62.784 280.573,61.94 C277.761,62.784 274.782,63.239 271.697,63.239 C254.663,63.239 240.854,49.431 240.854,32.398 C240.854,15.363 254.663,1.555 271.697,1.555" fill="#852124"/>
<path d="M300.104,3.445 C311.89,7.783 320.297,19.108 320.297,32.398 C320.297,49.431 306.489,63.239 289.454,63.239 C286.366,63.239 283.386,62.784 280.573,61.94 C277.761,62.784 274.782,63.239 271.697,63.239 C257.32,63.239 245.242,53.405 241.822,40.097 C251.466,53.606 263.565,57.28 278.381,50.083 C312.573,61.654 325.556,25.161 300.104,3.445" fill="#61191B"/>
<path d="M297.153,16.662 C294.196,13.79 290.182,11.311 285.934,11.371 C279.467,11.465 279.824,12.683 273.232,9.94 C269.746,8.489 266.511,8.766 262.892,9.656 C257.742,10.921 253.609,13.591 250.145,17.573 C243.862,25.22 245.932,33.326 255.406,36.683 L256.029,36.902 C258.651,37.773 266.73,40.311 268.992,39.858 C273.745,38.905 276.231,33.803 282.086,37.96 C286.883,41.431 287.62,43.618 294.196,42.936 C297.701,42.379 299.156,39.906 300.562,36.849 C303.38,30.721 302.722,23.53 298.527,18.185 C298.084,17.63 297.646,17.174 297.153,16.662" fill="#A02529"/>
<path d="M182.906,23.052 C186.623,23.052 190.21,23.601 193.595,24.617 C196.982,23.601 200.567,23.052 204.283,23.052 C224.79,23.052 241.411,39.675 241.411,60.179 C241.411,80.684 224.79,97.306 204.283,97.306 C200.567,97.306 196.982,96.758 193.595,95.742 C190.21,96.758 186.623,97.306 182.906,97.306 C162.401,97.306 145.778,80.684 145.778,60.179 C145.778,39.675 162.401,23.052 182.906,23.052" fill="#852124"/>
<path d="M217.107,25.326 C231.292,30.548 241.411,44.182 241.411,60.179 C241.411,80.684 224.79,97.306 204.283,97.306 C200.567,97.306 196.982,96.758 193.595,95.742 C190.21,96.758 186.623,97.306 182.906,97.306 C165.602,97.306 151.064,85.468 146.947,69.449 L146.947,69.448 C158.552,85.71 173.119,90.133 190.956,81.469 C232.115,95.399 247.744,51.467 217.107,25.326" fill="#61191B"/>
<path d="M213.552,41.237 C209.99,37.779 205.16,34.796 200.046,34.87 C192.261,34.98 192.691,36.448 184.757,33.146 C180.557,31.397 176.667,31.732 172.31,32.804 C166.109,34.328 161.138,37.54 156.962,42.333 C149.401,51.538 151.893,61.298 163.298,65.338 L164.046,65.602 C167.206,66.649 176.932,69.705 179.65,69.159 C185.374,68.012 188.365,61.87 195.415,66.875 C201.189,71.055 202.077,73.687 209.996,72.866 C214.212,72.195 215.963,69.218 217.655,65.538 C221.048,58.161 220.254,49.505 215.209,43.07 C214.669,42.402 214.146,41.853 213.552,41.237" fill="#A02529"/>
<path d="M88.513,390.871 C110.533,390.871 128.495,382.359 128.495,371.918 C128.495,361.479 110.533,352.964 88.513,352.964 C66.489,352.964 48.529,361.479 48.529,371.918 C48.529,382.359 66.489,390.871 88.513,390.871" fill="#A02529"/>
<path d="M58.132,311.686 C61.848,311.686 65.436,312.234 68.82,313.249 C72.207,312.234 75.795,311.686 79.511,311.686 C100.016,311.686 116.639,328.307 116.639,348.812 C116.639,369.319 100.016,385.942 79.511,385.942 C75.795,385.942 72.207,385.391 68.82,384.375 C65.436,385.391 61.848,385.942 58.132,385.942 C37.629,385.942 21.006,369.319 21.006,348.812 C21.006,328.307 37.629,311.686 58.132,311.686" fill="#852124"/>
<path d="M92.335,313.96 C106.521,319.182 116.639,332.816 116.639,348.812 C116.639,369.319 100.016,385.942 79.511,385.942 C75.795,385.942 72.207,385.391 68.82,384.375 C65.436,385.391 61.848,385.942 58.132,385.942 C40.828,385.942 26.289,374.101 22.174,358.082 C33.779,374.344 48.345,378.766 66.182,370.103 C107.344,384.033 122.97,340.101 92.335,313.96" fill="#61191B"/>
<path d="M88.779,329.871 C85.219,326.413 80.385,323.43 75.273,323.502 C67.489,323.615 67.919,325.082 59.982,321.779 C55.785,320.031 51.893,320.367 47.535,321.437 C41.339,322.96 36.362,326.173 32.19,330.967 C24.628,340.172 27.119,349.932 38.524,353.97 L39.272,354.237 C42.432,355.284 52.159,358.339 54.876,357.793 C60.602,356.645 63.591,350.503 70.641,355.509 C76.418,359.689 77.303,362.32 85.222,361.498 C89.438,360.827 91.188,357.851 92.881,354.171 C96.273,346.794 95.481,338.137 90.435,331.702 C89.898,331.035 89.373,330.488 88.779,329.871" fill="#A02529"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,65 @@
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
qt_internal_add_manual_test(painterPathQuickShape
GUI
SOURCES
main.cpp
debugpaintitem.h debugpaintitem.cpp
svgpathloader.h svgpathloader.cpp
debugvisualizationcontroller.h debugvisualizationcontroller.cpp
LIBRARIES
Qt::Gui
Qt::Qml
Qt::Quick
Qt::QuickPrivate
Qt::QuickShapesPrivate
Qt::SvgPrivate
)
set(qml_resource_files
"1535737773.svg"
"main.qml"
"SvgShape.qml"
"ControlPanel.qml"
"ControlPoint.qml"
"TextShape.qml"
"SimpleShape.qml"
"SmallPolygon.qml"
"Squircle.qml"
"ControlledShape.qml"
"Graziano.ttf"
"CubicShape.qml"
"hand-print.svg"
"background.png"
"arcDirection.qml"
"arcRotation.qml"
"capStyles.qml"
"cubicCurve.qml"
"dashPattern.qml"
"ellipticalArcs.qml"
"fillRules.qml"
"gradientSpreadModes.qml"
"joinStyles.qml"
"largeOrSmallArc.qml"
"linearGradient.qml"
"quadraticCurve.qml"
"radialGradient.qml"
"strokeOrFill.qml"
"text.qml"
"tiger.qml"
)
qt_internal_add_resource(painterPathQuickShape "qml"
PREFIX
"/"
FILES
${qml_resource_files}
)
qt_add_qml_module(painterPathQuickShape
VERSION 1.0
URI InstancingTest
RESOURCE_PREFIX /
)

View File

@ -0,0 +1,281 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Shapes
import QtQuick.Dialogs
Item {
property real scale: +scaleSlider.value.toFixed(4)
property color outlineColor: enableOutline.checked ? Qt.rgba(outlineColor.color.r, outlineColor.color.g, outlineColor.color.b, pathAlpha) : Qt.rgba(0,0,0,0)
property color fillColor: Qt.rgba(fillColor.color.r, fillColor.color.g, fillColor.color.b, pathAlpha)
property alias pathAlpha: alphaSlider.value
property alias outlineWidth: outlineWidth.value
property alias outlineStyle: outlineStyle.currentValue
property alias capStyle: capStyle.currentValue
property alias joinStyle: joinStyle.currentValue
property alias debugCurves: enableDebug.checked
property alias debugWireframe: enableWireframe.checked
property alias painterComparison: painterComparison.currentIndex
property alias painterComparisonColor: painterComparisonColor.color
property alias painterComparisonAlpha: painterComparisonColorAlpha.value
property alias outlineEnabled: enableOutline.checked
property alias gradientType: gradientType.currentIndex
property alias rendererName: rendererLabel.text
property alias preferCurve: rendererLabel.preferCurve
property int subShape: pickSubShape.checked ? subShapeSelector.value : -1
property real pathMargin: 150
function setScale(x) {
scaleSlider.value = x
}
signal pathChanged
function updatePath()
{
pathChanged()
}
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
RowLayout {
Label {
text: "Renderer:"
color: "white"
}
Label {
id: rendererLabel
property bool preferCurve: true
color: "white"
text: "Unknown"
TapHandler {
onTapped: {
rendererLabel.preferCurve = !rendererLabel.preferCurve
}
}
}
CheckBox { id: enableDebug }
Label {
text: "Debug"
color: "white"
}
CheckBox { id: enableWireframe }
Label {
text: "Wireframe"
color: "white"
}
ComboBox {
id: painterComparison
model: [
"No QPainter comparison",
"Overlaid QPainter comparison",
"Underlaid QPainter comparison"
]
}
Rectangle {
id: painterComparisonColor
color: "red"
width: 20
height: 20
MouseArea {
anchors.fill: parent
onClicked: {
painterComparisonColorDialog.open()
}
}
}
Slider {
id: painterComparisonColorAlpha
from: 0.0
to: 1.0
value: 1.0
}
Label {
text: "Alpha"
color: "white"
}
CheckBox { id: pickSubShape }
Label {
text: "Pick SVG sub-shape"
color: "white"
}
SpinBox {
id: subShapeSelector
visible: pickSubShape.checked
value: 0
to: 999
editable: true
}
}
RowLayout {
Label {
text: "Scale:"
color: "white"
}
TextField {
id: scaleEdit
text: scaleSlider.value.toFixed(4)
onEditingFinished: {
let val = +text
if (val > 0)
scaleSlider.value = val
}
}
Slider {
id: scaleSlider
Layout.fillWidth: true
from: 0.01
to: 500.0
value: 0.2
onValueChanged: scaleEdit.text = value.toFixed(4)
}
}
RowLayout {
Label {
text: "Fill color"
color: "white"
}
Rectangle {
id: fillColor
color: "#ffffff"
width: 20
height: 20
MouseArea {
anchors.fill: parent
onClicked: {
fillColorDialog.open()
}
}
}
ComboBox {
id: gradientType
model: [ "NoGradient", "LinearGradient", "RadialGradient", "ConicalGradient" ]
}
Label {
text: "Path alpha:"
color: "white"
}
Slider {
id: alphaSlider
Layout.fillWidth: true
from: 0.0
to: 1.0
value: 1.0
}
}
RowLayout {
CheckBox {
id: enableOutline
text: "Enable outline"
palette.windowText: "white"
}
RowLayout {
opacity: enableOutline.checked ? 1 : 0
Label {
text: "Outline color:"
color: "white"
}
Rectangle {
id: outlineColor
property color selectedColor: "#33ccbb"
color: selectedColor
width: 20
height: 20
MouseArea {
anchors.fill: parent
onClicked: {
outlineColorDialog.open()
}
}
}
ComboBox {
id: outlineStyle
textRole: "text"
valueRole: "style"
model: ListModel {
ListElement {
text: "Solid line"
style: ShapePath.SolidLine
}
ListElement {
text: "Dash line"
style: ShapePath.DashLine
}
}
}
ComboBox {
id: joinStyle
textRole: "text"
valueRole: "style"
model: ListModel {
ListElement {
text: "Miter join"
style: ShapePath.MiterJoin
}
ListElement {
text: "Bevel join"
style: ShapePath.BevelJoin
}
ListElement {
text: "Round join"
style: ShapePath.RoundJoin
}
}
}
ComboBox {
id: capStyle
textRole: "text"
valueRole: "style"
model: ListModel {
ListElement {
text: "Square cap"
style: ShapePath.SquareCap
}
ListElement {
text: "Round cap"
style: ShapePath.RoundCap
}
ListElement {
text: "Flat cap"
style: ShapePath.FlatCap
}
}
}
Label {
text: "Outline width"
color: "white"
}
Slider {
id: outlineWidth
Layout.fillWidth: true
from: 0.0
to: 30.0
value: 10.0
}
}
}
}
ColorDialog {
id: outlineColorDialog
selectedColor: outlineColor.selectedColor
onAccepted: outlineColor.selectedColor = selectedColor
}
ColorDialog {
id: fillColorDialog
selectedColor: fillColor.color
onAccepted: fillColor.color = selectedColor
}
ColorDialog {
id: painterComparisonColorDialog
selectedColor: painterComparisonColor.color
onAccepted: painterComparisonColor.color = selectedColor
}
}

View File

@ -0,0 +1,47 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
Rectangle {
id: point1
color: "red"
border.width: 1
border.color: "black"
opacity: 0.3
width: 20
height: 20
property real cx: 400
property real cy: 800
property point pt: Qt.point(cx, cy)
DragHandler {
xAxis.minimum: -controlPanel.pathMargin
yAxis.minimum: -controlPanel.pathMargin
}
onXChanged: {
cx = (x + width/2) / controlPanel.scale
controlPanel.updatePath()
}
onYChanged: {
cy = (y + height/2) / controlPanel.scale
controlPanel.updatePath()
}
Component.onCompleted: {
x = cx * controlPanel.scale - width/2
y = cy * controlPanel.scale - height/2
}
Connections {
target: controlPanel
function onScaleChanged() {
x = cx * controlPanel.scale - width/2
y = cy * controlPanel.scale - height/2
}
}
}

View File

@ -0,0 +1,134 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
import io.qt
Item {
id: topLevel
property alias fillColor: shapePath.fillColor
property alias strokeStyle: shapePath.strokeStyle
property alias capStyle: shapePath.capStyle
property alias strokeColor: shapePath.strokeColor
property alias strokeWidth: shapePath.strokeWidth
property alias fillRule: shapePath.fillRule
property alias startX: shapePath.startX
property alias startY: shapePath.startY
property rect boundingRect: shape.boundingRect
width: boundingRect.width
height: boundingRect.height
property vector2d startPoint: "0,0"
property list<QtObject> delegate
LinearGradient {
id: linearGradient
x1: shape.boundingRect.left
y1: shape.boundingRect.top
x2: shape.boundingRect.right
y2: shape.boundingRect.bottom
GradientStop { position: 0; color: fillColor }
GradientStop { position: 1; color: Qt.rgba(1 - fillColor.r, 1 - fillColor.g, 1 - fillColor.b, 1) }
}
RadialGradient {
id: radialGradient
centerX: 0.5 * (shape.boundingRect.right + shape.boundingRect.left)
centerY: 0.5 * (shape.boundingRect.top + shape.boundingRect.bottom)
focalX: centerX
focalY: centerY
centerRadius: 0.5 * (shape.boundingRect.right - shape.boundingRect.left)
focalRadius: 0.1
GradientStop { position: 0.0; color: fillColor }
GradientStop { position: 1.0; color: Qt.rgba(1 - fillColor.r, 1 - fillColor.g, 1 - fillColor.b, 1) }
}
ConicalGradient {
id: conicalGradient
centerX: 0.5 * (shape.boundingRect.right + shape.boundingRect.left)
centerY: 0.5 * (shape.boundingRect.top + shape.boundingRect.bottom)
GradientStop { position: 0.0; color: fillColor }
GradientStop { position: 0.5; color: Qt.rgba(1 - fillColor.r, 1 - fillColor.g, 1 - fillColor.b, 1) }
GradientStop { position: 1.0; color: fillColor }
}
property var gradients: [ null, linearGradient, radialGradient, conicalGradient ]
Shape {
id: shape
x: 0
y: 0
preferredRendererType: controlPanel.preferCurve ? Shape.CurveRenderer : Shape.UnknownRenderer
onRendererTypeChanged: {
controlPanel.rendererName = rendererType == Shape.SoftwareRenderer ? "Software" :
rendererType == Shape.GeometryRenderer ? "Geometry" :
rendererType == Shape.CurveRenderer ? "Curve" : "Unknown";
}
transform: [
Scale {
xScale: controlPanel.scale
yScale: controlPanel.scale
origin.x: shape.implicitWidth / 2
origin.y: shape.implicitHeight / 2
}
]
ShapePath {
id: shapePath
fillRule: ShapePath.WindingFill
fillGradient: gradients[controlPanel.gradientType]
strokeColor: controlPanel.outlineColor
fillColor: controlPanel.fillColor
strokeWidth: controlPanel.outlineWidth
strokeStyle: controlPanel.outlineStyle
joinStyle: controlPanel.joinStyle
capStyle: controlPanel.capStyle
}
Repeater {
model: topLevel.delegate
onModelChanged: {
shapePath.pathElements = []
for (var i = 0; i < model.length; ++i)
shapePath.pathElements.push(model[i])
}
}
}
Connections {
target: controlPanel
function onPathChanged() {
debugPaintPath.update()
}
}
DebugPaintItem {
id: debugPaintPath
shape: shapePath
visible: controlPanel.painterComparison > 0
color: controlPanel.painterComparisonColor
opacity: controlPanel.painterComparisonAlpha
z: controlPanel.painterComparison > 1 ? -1 : 0
pathScale: controlPanel.scale
fillRule: topLevel.fillRule
strokeStyle: topLevel.strokeStyle
strokeColor: controlPanel.outlineEnabled ? Qt.rgba(1 - color.r, 1 - color.g, 1 - color.b, 1) : "transparent"
capStyle: topLevel.capStyle
joinStyle: controlPanel.joinStyle
strokeWidth: topLevel.strokeWidth
width: visible ? (shape.boundingRect.width + shape.boundingRect.x) * controlPanel.scale : 1
height: visible ? (shape.boundingRect.height + shape.boundingRect.y) * controlPanel.scale : 1
}
DebugVisualizationController {
showCurves: controlPanel.debugCurves
showWireframe: controlPanel.debugWireframe
onSettingsChanged: { shapePath.changed() }
}
}

View File

@ -0,0 +1,73 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
ControlledShape {
fillRule: ShapePath.OddEvenFill
delegate: [
PathMove { x: start.cx; y: start.cy},
PathCubic { x: end.cx; y: end.cy;
control1X: control1.cx; control1Y: control1.cy
control2X: control2.cx; control2Y: control2.cy },
PathMove { x: start2.cx; y: start2.cy },
PathCubic { x: end2.cx; y: end2.cy;
control1X: control21.cx; control1Y: control21.cy
control2X: control22.cx; control2Y: control22.cy },
PathLine { x: lineEnd.cx; y: lineEnd.cy }
]
// Cubic path 1
ControlPoint {
id: start
cx: 200
cy: 400
}
ControlPoint {
id: control1
color: "blue"
cx: 800
cy: 0
}
ControlPoint {
id: control2
color: "blue"
cx: 800
cy: 1000
}
ControlPoint {
id: end
cx: 200
cy: 600
}
// Cubic path 2
ControlPoint {
id: start2
cx: 2200
cy: 200
}
ControlPoint {
id: control21
color: "blue"
cx: 1200
cy: 600
}
ControlPoint {
id: control22
color: "blue"
cx: 3200
cy: 1000
}
ControlPoint {
id: end2
cx: 2200
cy: 1400
}
ControlPoint {
id: lineEnd
cx: 1200
cy: 200
}
}

View File

@ -0,0 +1,22 @@
1. FONTLOG for Graziano
This file provides detailed information on the Graziano
Font Software. This information should be distributed along with
the Graziano font and any derivative works.
2. Basic Font Information
A font based on my left-handed handwriting.
To take notes with Block and lowercase letters on plain
white paper without any guideline (with a large-tipped pen)
is a good training ground for writing.
3. ChangeLog
28 May 2011 (Graziano Capelli) Graziano version 1.0.
- Initial release
4 Acknowledgements
None yet. Feature requests, bug reports and contributions should be
sent to Graziano Capelli (air at shweb dot it; femtosoft at libero dot it)

Binary file not shown.

View File

@ -0,0 +1,534 @@
OFL FAQ - Frequently Asked Questions about the SIL Open Font License (OFL)
Version 1.1-update1 - 31 March 2009
(See http://scripts.sil.org/OFL for updates)
1 ABOUT USING AND DISTRIBUTING FONTS LICENSED UNDER THE OFL
1.1 Can I use the fonts in any publication, even embedded in the file?
Yes. You may use them like most other fonts, but unlike some fonts you may
include an embedded subset of the fonts in your document. Such use does not
require you to include this license or other files (listed in OFL condition 2),
nor does it require any type of acknowledgement within the publication. Some
mention of the font name within the publication information (such as in a
colophon) is usually appreciated. If you wish to include the complete font as a
separate file, you should distribute the full font package, including all
existing acknowledgements, and comply with the OFL conditions. Of course,
referencing or embedding an OFL font in any document does not change the
license of the document itself. The requirement for fonts to remain under the
OFL does not apply to any document created using the fonts and their
derivatives. Similarly, creating any kind of graphic using a font under OFL
does not make the resulting artwork subject to the OFL.
1.2 Can I make web pages using these fonts?
Yes! Go ahead! Using CSS (Cascading Style Sheets) is recommended. Direct usage
of fonts retrieved from a remote server - also referred to as font linking -
using cross-platform open standards like @font-face is encouraged. This is
considered to be use and distribution for which the OFL explicitly grants
permission. The font file itself is not embedded in the webpage but referenced
through a web address (i.e. a URI regardless of file format or access protocol)
which will cause the browser to retrieve and use the corresponding font to
render the webpage. This usage scenario is different from font embedding within
a document (i.e. a PDF) where the font or some of its elements become part of
the document. Note that embedding in a document is also permitted by the
license as indicated in 1.1. (See 1.10 for details related to URL-based access
restrictions methods or DRM mechanisms).
1.3 Can I make the fonts available to others from my web site?
Yes, as long as you meet the conditions of the license (do not sell by itself,
include the necessary files, include the necessary copyright and license
information, rename Modified Versions, do not abuse the Author(s)' name(s) and
do not sublicense). In the case where you are hosting fonts to be served on the
web, make sure the file contains the needed copyright notice(s) and licensing
information in its metadata. Please double-check the accuracy of every field to
prevent contradictory information. If you are making the font available for use
via the @font-face open standard, putting this information in the standard font
metadata fields is sufficient. Other font formats, including EOT and proposed
superior alternatives, already provide fields for this information.
1.4 Can the fonts be included with Free/Libre and Open Source Software
collections such as GNU/Linux and BSD distributions?
Yes! Fonts licensed under the OFL can be freely aggregated with software under
FLOSS (Free/Libre and Open Source Software) licenses. Since fonts are much more
useful aggregated to than merged with existing software, possible
incompatibility with existing software licenses is not a problem. You can also
repackage the fonts and the accompanying components in a .rpm or .deb package
and include them in distro CD/DVDs and online repositories.
1.5 I want to distribute the fonts with my program. Does this mean my program
also has to be free/libre and open source software?
No. Only the portions based on the Font Software are required to be released
under the OFL. The intent of the license is to allow aggregation or bundling
with software under restricted licensing as well.
1.6 Can I include the fonts on a CD of freeware or commercial fonts?
Yes, as long some other font or software is also on the disk, so the OFL font
is not sold by itself.
1.7 Can I sell a software package that includes these fonts?
Yes, you can do this with both the Original Version and a Modified Version.
Examples of bundling made possible by the OFL would include: word processors,
design and publishing applications, training and educational software,
edutainment software, etc.
1.8 Why won't the OFL let me sell the fonts alone?
The intent is to keep people from making money by simply redistributing the
fonts. The only people who ought to profit directly from the fonts should be
the original authors, and those authors have kindly given up potential direct
income to distribute their fonts under the OFL. Please honor and respect their
contribution!
1.9 I've come across a font released under the OFL. How can I easily get more
information about the Original Version? How can I know where it stands compared
to the Original Version or other Modified Versions?
Consult the copyright statement(s) in the license for ways to contact the
original authors. Consult the FONTLOG for information on how the font differs
from the Original Version, and get in touch with the various contributors via
the information in the acknowledgment section. Please consider using the
Original Versions of the fonts whenever possible.
1.10 What do you mean in condition 4? Can you provide examples of abusive
promotion / endorsement / advertisement vs. normal acknowledgement?
The intent is that the goodwill and reputation of the author(s) should not be
used in a way that makes it sound like the original author(s) endorse or
approve of a specific Modified Version or software bundle. For example, it
would not be right to advertise a word processor by naming the author(s) in a
listing of software features, or to promote a Modified Version on a web site by
saying "designed by ...". However, it would be appropriate to acknowledge the
author(s) if your software package has a list of people who deserve thanks. We
realize that this can seem to be a gray area, but the standard used to judge an
acknowledgement is that if the acknowledgement benefits the author(s) it is
allowed, but if it primarily benefits other parties, or could reflect poorly on
the author(s), then it is not.
1.11 Can Font Software released under the OFL be subject to URL-based access
restrictions methods or DRM mechanisms?
Yes, but these issues are out-of-scope for the OFL. The license itself neither
encourages their use nor prohibits them since such mechanisms are not
implemented in the components of the Font Software but through external
software. Such restrictions are put in place for many different purposes
corresponding to various usage scenarios. One common example is to limit
potentially dangerous cross-site scripting attacks. However, in the spirit of
libre/open fonts and unrestricted writing systems, we strongly encourage open
sharing and reuse of OFL fonts, and the establishment of an environment where
such restrictions are unnecessary. Note that whether you wish to use such
mechanisms or you prefer not to, you must still abide by the rules set forth by
the OFL when using fonts released by their authors under this license.
Derivative fonts must be licensed under the OFL, even if they are part of a
service for which you charge fees and/or for which access to source code is
restricted. You may not sell the fonts on their own - they must be part of a
larger software package or bundle. For example, even if the OFL font is
distributed in a software package or via an online service using a DRM
mechanism, the user would still have the right to extract that font, use,
study, modify and redistribute it under the OFL.
1.12 What about distributing fonts with a document? Within a compressed folder
structure like an OpenDocument file (.odt) for example? Is it redistribution,
bundling or embedding?
The vast majority of the time, documents circulated in electronic form
reference a font name which will match the corresponding font on the target
system but do not carry the font within themselves. There may, however, be some
cases where you need to bundle a font with the document. Certain document
formats may allow the inclusion of an unmodified font within their file
structure which consists of a compressed folder containing the various
resources forming the document (such as pictures and thumbnails). Including
fonts within such a structure is understood as being different from embedding
but rather similar to bundling (or mere aggregation) for which the licensing
makes explicit provision. In this case the font is conveyed unchanged whereas
embedding a font transforms it from the original format. The OFL does not allow
anyone to extract the font from such a structure to then redistribute it under
another license. The explicit permission to redistribute and embed does not
cancel the requirement for the Font Software to remain under the license chosen
by its Author(s).
1.13 If OFL fonts are extracted from a document in which they are embedded
(such as a PDF file), what can be done with them? Is this a risk to Author(s)?
The few utilities that can extract fonts embedded in a PDF will only output
limited amounts of outlines - not a complete font. To create a working font
from this method is much more difficult than finding the source of the original
OFL font. So there is little chance that an OFL font would be extracted and
redistributed inappropriately through this method. Even so, copyright laws
address any misrepresentation of authorship. All Font Software released under
the OFL and marked as such by the Author(s) is intended to remain under this
license regardless of the distribution method, and cannot be redistributed
under any other license. We strongly discourage any font extraction - we
recommend directly using the font sources instead - but if you extract font
outlines from a document please be considerate, use your common sense and
respect the work of the Author(s) and the licensing model.
1.14 What about sharing OFL fonts with friends on a CD, DVD or USB stick?
You are very welcome to share open fonts with friends, family and colleagues on
such removable media. Please make sure that you share and share-alike as much
as possible from what the Author(s) released and that you don't strip away
useful information which may not be present in the binary font files
themselves. Just remember that in the case where you sell the font, it has to
come bundled with software.
2 ABOUT MODIFYING OFL LICENSED FONTS
2.1 Can I change the fonts? Are there any limitations to what things I can and
cannot change?
You are allowed to change anything, as long as such changes do not violate the
terms of the license. In other words, you are not allowed to remove the
copyright statement(s) from the font, but you could add additional information
into it that covers your contribution.
2.2 I have a font that needs a few extra glyphs - can I take them from an OFL
licensed font and copy them into mine?
Yes, but if you distribute that font to others it must be under the OFL, and
include the information mentioned in condition 2 of the license.
2.3 Can I charge people for my additional work? In other words, if I add a
bunch of special glyphs and/or OpenType/Graphite code, can I sell the enhanced
font?
Not by itself. Derivative fonts must be released under the OFL and cannot be
sold by themselves. It is permitted, however, to include them in a larger
software package (such as text editors, office suites or operating systems),
even if the larger package is sold. In that case, you are strongly encouraged,
but not required, to also make that derived font easily and freely available
outside of the larger package.
2.4 Can I pay someone to enhance the fonts for my use and distribution?
Yes. This is a good way to fund the further development of the fonts. Keep in
mind, however, that if the font is distributed to others it must be under the
OFL. You won't be able to recover your investment by exclusively selling the
font, but you will be making a valuable contribution to the community. Please
remember how you have benefitted from the contributions of others.
2.5 I need to make substantial revisions to the font to make it work with my
program. It will be a lot of work, and a big investment, and I want to be sure
that it can only be distributed with my program. Can I restrict its use?
No. If you redistribute a Modified Version of the font it must be under the
OFL. You may not restrict it in any way. This is intended to ensure that all
released improvements to the fonts become available to everyone. But you will
likely get an edge over competitors by being the first to distribute a bundle
with the enhancements. Again, please remember how you have benefitted from the
contributions of others.
2.6 Do I have to make any derivative fonts (including extended source files,
build scripts, documentation, etc.) publicly available?
No, but please do share your improvements with others. You may find that you
receive more than what you gave in return.
2.7 Why can't I use the Reserved Font Name(s) in my derivative font names? I'd
like people to know where the design came from.
The best way to acknowledge the source of the design is to thank the original
authors and any other contributors in the files that are distributed with your
revised font (although no acknowledgement is required). The FONTLOG is a
natural place to do this. Reserved Font Name(s) ensure that the only fonts that
have the original names are the unmodified Original Versions. This allows
designers to maintain artistic integrity while allowing collaboration to
happen. It eliminates potential confusion and name conflicts. When choosing a
name be creative and avoid names that reuse almost all the same letters in the
same order or sound like the original. Keep in mind that the Copyright
Holder(s) can allow a specific trusted partner to use Reserved Font Name(s)
through a separate written agreement.
2.8 What do you mean by "primary name as presented to the user"? Are you
referring to the font menu name?
Yes, the requirement to change the visible name used to differentiate the font
from others applies to the font menu name and other mechanisms to specify a
font in a document. It would be fine, for example, to keep a text reference to
the original fonts in the description field, in your modified source file or in
documentation provided alongside your derivative as long as no one could be
confused that your modified source is the original. But you cannot use the
Reserved Font Names in any way to identify the font to the user (unless the
Copyright Holder(s) allow(s) it through a separate agreement; see section 2.7).
Users who install derivatives ("Modified Versions") on their systems should not
see any of the original names ("Reserved Font Names") in their font menus, for
example. Again, this is to ensure that users are not confused and do not
mistake a font for another and so expect features only another derivative or
the Original Version can actually offer. Ultimately, creating name conflicts
will cause many problems for the users as well as for the designer of both the
Original and Modified versions, so please think ahead and find a good name for
your own derivative. Font substitution systems like fontconfig, or
application-level font fallback configuration within OpenOffice.org or Scribus,
will also get very confused if the name of the font they are configured to
substitute to actually refers to another physical font on the user's hard
drive. It will help everyone if Original Versions and Modified Versions can
easily be distinguished from one another and from other derivatives. The
substitution mechanism itself is outside the scope of the license. Users can
always manually change a font reference in a document or set up some kind of
substitution at a higher level but at the lower level the fonts themselves have
to respect the Reserved Font Name(s) requirement to prevent ambiguity. If a
substitution is currently active the user should be aware of it.
2.9 Am I not allowed to use any part of the Reserved Font Names?
You may not use the words of the font names, but you would be allowed to use
parts of words, as long as you do not use any word from the Reserved Font Names
entirely. We do not recommend using parts of words because of potential
confusion, but it is allowed. For example, if "Foobar" was a Reserved Font
Name, you would be allowed to use "Foo" or "bar", although we would not
recommend it. Such an unfortunate choice would confuse the users of your fonts
as well as make it harder for other designers to contribute.
2.10 So what should I, as an author, identify as Reserved Font Names?
Original authors are encouraged to name their fonts using clear, distinct
names, and only declare the unique parts of the name as Reserved Font Names.
For example, the author of a font called "Foobar Sans" would declare "Foobar"
as a Reserved Font Name, but not "Sans", as that is a common typographical
term, and may be a useful word to use in a derivative font name. Reserved Font
Names should also be single words. A font called "Flowing River" should have
Reserved Font Names "Flowing" and "River", not "Flowing River".
2.11 Do I, as an author, have to identify any Reserved Font Names?
No, but we strongly encourage you to do so. This is to avoid confusion between
your work and Modified versions. You may, however, give certain trusted parties
the right to use any of your Reserved Font Names through separate written
agreements. For example, even if "Foobar" is a RFN, you could write up an
agreement to give company "XYZ" the right to distribute a modified version with
a name that includes "Foobar". This allows for freedom without confusion.
2.12 Are any names (such as the main font name) reserved by default?
No. That is a change to the license as of version 1.1. If you want any names to
be Reserved Font Names, they must be specified after the copyright statement(s).
2.13 What is this FONTLOG thing exactly?
It has three purposes: 1) to provide basic information on the font to users and
other developers, 2) to document changes that have been made to the font or
accompanying files, either by the original authors or others, and 3) to provide
a place to acknowledge the authors and other contributors. Please use it! See
below for details on how changes should be noted.
2.14 Am I required to update the FONTLOG?
No, but users, designers and other developers might get very frustrated at you
if you don't! People need to know how derivative fonts differ from the
original, and how to take advantage of the changes, or build on them.
3 ABOUT THE FONTLOG
The FONTLOG can take a variety of formats, but should include these four
sections:
3.1 FONTLOG for <FontFamilyName>
This file provides detailed information on the <FontFamilyName> Font Software.
This information should be distributed along with the <FontFamilyName> fonts
and any derivative works.
3.2 Basic Font Information
(Here is where you would describe the purpose and brief specifications for the
font project, and where users can find more detailed documentation. It can also
include references to how changes can be contributed back to the Original
Version. You may also wish to include a short guide to the design, or a
reference to such a document.)
3.3 ChangeLog
(This should list both major and minor changes, most recent first. Here are
some examples:)
7 February 2007 (Pat Johnson) <NewFontFamilyName> Version 1.3
- Added Greek and Cyrillic glyphs
- Released as "<NewFontFamilyName>"
7 March 2006 (Fred Foobar) <NewFontFamilyName> Version 1.2
- Tweaked contextual behaviours
- Released as "<NewFontFamilyName>"
1 Feb 2005 (Jane Doe) <NewFontFamilyName> Version 1.1
- Improved build script performance and verbosity
- Extended the smart code documentation
- Corrected minor typos in the documentation
- Fixed position of combining inverted breve below (U+032F)
- Added OpenType/Graphite smart code for Armenian
- Added Armenian glyphs (U+0531 -> U+0587)
- Released as "<NewFontFamilyName>"
1 Jan 2005 (Joe Smith) <FontFamilyName> Version 1.0
- Initial release of font "<FontFamilyName>"
3.4 Acknowledgements
(Here is where contributors can be acknowledged.
If you make modifications be sure to add your name (N), email (E), web-address
(W) and description (D). This list is sorted by last name in alphabetical
order.)
N: Jane Doe
E: jane@university.edu
W: http://art.university.edu/projects/fonts
D: Contributor - Armenian glyphs and code
N: Fred Foobar
E: fred@foobar.org
W: http://foobar.org
D: Contributor - misc Graphite fixes
N: Pat Johnson
E: pat@fontstudio.org
W: http://pat.fontstudio.org
D: Designer - Greek & Cyrillic glyphs based on Roman design
N: Tom Parker
E: tom@company.com
W: http://www.company.com/tom/projects/fonts
D: Engineer - original smart font code
N: Joe Smith
E: joe@fontstudio.org
W: http://joe.fontstudio.org
D: Designer - original Roman glyphs
(Original authors can also include information here about their organization.)
4 ABOUT MAKING CONTRIBUTIONS
4.1 Why should I contribute my changes back to the original authors?
It would benefit many people if you contributed back to what you've received.
Providing your contributions and improvements to the fonts and other components
(data files, source code, build scripts, documentation, etc.) could be a
tremendous help and would encourage others to contribute as well and 'give
back', which means you will have an opportunity to benefit from other people's
contributions as well. Sometimes maintaining your own separate version takes
more effort than merging back with the original. Be aware that any
contributions, however, must be either your own original creation or work that
you own, and you may be asked to affirm that clearly when you contribute.
4.2 I've made some very nice improvements to the font, will you consider
adopting them and putting them into future Original Versions?
Most authors would be very happy to receive such contributions. Keep in mind
that it is unlikely that they would want to incorporate major changes that
would require additional work on their end. Any contributions would likely need
to be made for all the fonts in a family and match the overall design and
style. Authors are encouraged to include a guide to the design with the fonts.
It would also help to have contributions submitted as patches or clearly marked
changes (the use of smart source revision control systems like subversion, svk,
mercurial, git or bzr is a good idea). Examples of useful contributions are bug
fixes, additional glyphs, stylistic alternates (and the smart font code to
access them) or improved hinting.
4.3 How can I financially support the development of OFL fonts?
It is likely that most authors of OFL fonts would accept financial
contributions - contact them for instructions on how to do this. Such
contributions would support future development. You can also pay for others to
enhance the fonts and contribute the results back to the original authors for
inclusion in the Original Version.
5 ABOUT THE LICENSE
5.1 I see that this is version 1.1 of the license. Will there be later changes?
Version 1.1 is the first minor revision of the OFL. We are confident that
version 1.1 will meet most needs, but are open to future improvements. Any
revisions would be for future font releases, and previously existing licenses
would remain in effect. No retroactive changes are possible, although the
Copyright Holder(s) can re-release the font under a revised OFL. All versions
will be available on our web site: http://scripts.sil.org/OFL.
5.2 Can I use the SIL Open Font License for my own fonts?
Yes! We heartily encourage anyone to use the OFL to distribute their own
original fonts. It is a carefully constructed license that allows great freedom
along with enough artistic integrity protection for the work of the authors as
well as clear rules for other contributors and those who redistribute the
fonts. Some additional information about using the OFL is included at the end
of this FAQ.
5.3 Does this license restrict the rights of the Copyright Holder(s)?
No. The Copyright Holder(s) still retain(s) all the rights to their creation;
they are only releasing a portion of it for use in a specific way. For example,
the Copyright Holder(s) may choose to release a 'basic' version of their font
under the OFL, but sell a restricted 'enhanced' version. Only the Copyright
Holder(s) can do this.
5.4 Is the OFL a contract or a license?
The OFL is a license and not a contract and so does not require you to sign it
to have legal validity. By using, modifying and redistributing components under
the OFL you indicate that you accept the license.
5.5 How about translating the license and the FAQ into other languages?
SIL certainly recognises the need for people who are not familiar with English
to be able to understand the OFL and this FAQ better in their own language.
Making the license very clear and readable is a key goal of the OFL.
If you are an experienced translator, you are very welcome to help by
translating the OFL and its FAQ so that designers and users in your language
community can understand the license better. But only the original English
version of the license has legal value and has been approved by the community.
Translations do not count as legal substitutes and should only serve as a way
to explain the original license. SIL - as the author and steward of the license
for the community at large - does not approve any translation of the OFL as
legally valid because even small translation ambiguities could be abused and
create problems.
We give permission to publish unofficial translations into other languages
provided that they comply with the following guidelines:
- put the following disclaimer in both English and the target language stating
clearly that the translation is unofficial:
"This is an unofficial translation of the SIL Open Font License into $language.
It was not published by SIL International, and does not legally state the
distribution terms for fonts that use the OFL. A release under the OFL is only
valid when using the original English text.
However, we recognize that this unofficial translation will help users and
designers not familiar with English to understand the SIL OFL better and make
it easier to use and release font families under this collaborative font design
model. We encourage designers who consider releasing their creation under the
OFL to read the FAQ in their own language if it is available.
Please go to http://scripts.sil.org/OFL for the official version of the license
and the accompanying FAQ."
- keep your unofficial translation current and update it at our request if
needed, for example if there is any ambiguity which could lead to confusion.
If you start such a unofficial translation effort of the OFL and its
accompanying FAQ please let us know, thank you.
6 ABOUT SIL INTERNATIONAL
6.1 Who is SIL International and what does it do?
SIL International is a worldwide faith-based education and development
organization (NGO) that studies, documents, and assists in developing the
world's lesser-known languages through literacy, linguistics, translation, and
other academic disciplines. SIL makes its services available to all without
regard to religious belief, political ideology, gender, race, or ethnic
background. SIL's members and volunteers share a Christian commitment.
6.2 What does this have to do with font licensing?
The ability to read, write, type and publish in one's own language is one of
the most critical needs for millions of people around the world. This requires
fonts that are widely available and support lesser-known languages. SIL
develops - and encourages others to develop - a complete stack of writing
systems implementation components available under open licenses. This open
stack includes input methods, smart fonts, smart rendering libraries and smart
applications. There has been a need for a common open license that is
specifically applicable to fonts and related software (a crucial component of
this stack) so SIL developed the SIL Open Font License with the help of the
FLOSS community.
6.3 How can I contact SIL?
Our main web site is: http://www.sil.org/
Our site about complex scripts is: http://scripts.sil.org/
Information about this license (including contact email information) is at:
http://scripts.sil.org/OFL
7 ABOUT USING THE OFL FOR YOUR ORIGINAL FONTS
If you want to release your fonts under the OFL, you only need to do the
following:
7.1 Put your copyright and reserved font names information in the beginning of
the main OFL file (simply use the dedicated placeholders).
7.2 Put your copyright and the OFL references in your various font files (such
as in the copyright, license and description fields) and in your other
components (build scripts, glyph databases, documentation, rendering samples,
etc). Accurate metadata in your font files is beneficial to you as an
increasing number of applications are exposing this information to the user.
For example, clickable links can bring users back to your website and let them
know about other work you have done or services you provide. Depending on the
format of your fonts and sources, you can use template human-readable headers
or machine-readable metadata.
7.3 Write an initial FONTLOG for your font and include it in the release
package.
7.4 Include the OFL license file in your release package.
7.5 We also highly recommend you include the relevant practical documentation
on the license by putting the OFL-FAQ in your package.
7.6 If you wish, you can use the OFL Graphics on your web page.
That's all. If you have any more questions please get in touch with us.

View File

@ -0,0 +1,93 @@
Copyright (c) 2011, Graziano Capelli (air@shweb.it; femtosoft@libero.it).
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@ -0,0 +1,7 @@
SVGs included:
peace_victory.svg: Public Domain by OpenClipart (https://freesvg.org/peace-sign)
hand-print.svg: Public Domain by OpenClipart (https://freesvg.org/palm-print-in-black-and-white)
1535737773.svg: Public Domain by publicdomainvectors.org (https://freesvg.org/piece-of-cake-4)
Fonts included:
Graziano.ttf: by Graziano Capelli (https://fontlibrary.org/en/font/graziano), OFL (SIL Open Font License)

View File

@ -0,0 +1,117 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
ControlledShape {
fillRule: ShapePath.OddEvenFill
delegate: [
// A triangle
PathPolyline {
id: ppl
path: [ Qt.point(100.0, 100.0),
Qt.point(1250.0, 150.0),
Qt.point(100.0, 1000.0),
Qt.point(100, 100)
]
},
// A very narrow shape with one convex and one concave curve
PathMove { x: 600; y: 1200},
PathQuad { x: 800; y: 1200; controlX: 700; controlY: 600 },
PathQuad { x: 600; y: 1200; controlX: 700; controlY: 700 },
// A more complex path with editable points
PathMove { x: p1.cx; y: p1.cy },
PathQuad { x: p2.cx; y: p2.cy; controlX: c1.cx; controlY: c1.cy },
PathQuad { x: p3.cx; y: p3.cy; controlX: c2.cx; controlY: c2.cy },
PathQuad { x: p4.cx; y: p4.cy; controlX: c3.cx; controlY: c3.cy },
PathLine { x: p5.cx; y: p5.cy },
PathQuad { x: p6.cx; y: p6.cy; controlX: c5.cx; controlY: c5.cy },
PathQuad { x: p7.cx; y: p7.cy; controlX: c6.cx; controlY: c6.cy },
PathQuad { x: p8.cx; y: p8.cy; controlX: c7.cx; controlY: c7.cy }
]
// Control points for the editable part:
// Curve p1-c1-p2, Curve p2-c2-p3, Curve p3-c3-p4
// Line p4-p5, Curve p5-c5-p6, Curve p6-c6-p7, Curve p7-c7-p8
ControlPoint {
id: p1
cx: 100
cy: 1000
}
ControlPoint {
id: c1
color: "blue"
cx: 200
cy: 1500
}
ControlPoint {
id: p2
cx: 700
cy: 1500
}
ControlPoint {
id: c2
color: "blue"
cx: 1200
cy: 1500
}
ControlPoint {
id: p3
cx: 1200
cy: 1000
}
ControlPoint {
id: c3
color: "blue"
cx: 1100
cy: 700
}
ControlPoint {
id: p4
cx: 800
cy: 600
}
ControlPoint {
id: p5
cx: 800
cy: 800
}
ControlPoint {
id: c5
color: "blue"
cx: 1000
cy: 600
}
ControlPoint {
id: p6
cx: 1000
cy: 1000
}
ControlPoint {
id: c6
color: "blue"
cx: 1000
cy: 1300
}
ControlPoint {
id: p7
cx: 700
cy: 1300
}
ControlPoint {
id: c7
color: "blue"
cx: 400
cy: 1300
}
ControlPoint {
id: p8
cx: 400
cy: 1000
}
}

View File

@ -0,0 +1,49 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
ControlledShape {
fillRule: ShapePath.OddEvenFill
delegate: [
PathPolyline {
path: [ point1.pt,
point2.pt,
point3.pt,
point4.pt,
point5.pt,
point1.pt
]
}
]
ControlPoint {
id: point1
cx: 400
cy: 900
}
ControlPoint {
id: point2
cx: 1500
cy: 600
}
ControlPoint {
id: point3
cx: 2500
cy: 1100
}
ControlPoint {
id: point4
cx: 3000
cy: 1100
}
ControlPoint {
id: point5
cx: 2000
cy: 1900
}
}

View File

@ -0,0 +1,60 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
ControlledShape {
delegate: [
PathMove { x: p1.cx; y: p1.cy },
PathQuad { x: p2.cx; y: p2.cy; controlX: c1.cx; controlY: c1.cy },
PathQuad { x: p3.cx; y: p3.cy; controlX: c2.cx; controlY: c2.cy },
PathQuad { x: p4.cx; y: p4.cy; controlX: c3.cx; controlY: c3.cy },
//PathQuad { x: 600; y: 100; controlX: 100; controlY: 100 },
PathLine { x: p5.cx; y: p5.cy }
]
ControlPoint {
id: p1
cx: 600
cy: 100
}
ControlPoint {
id: c1
color: "blue"
cx: 1100
cy: 100
}
ControlPoint {
id: p2
cx: 1100
cy: 600
}
ControlPoint {
id: c2
color: "blue"
cx: 1100
cy: 1100
}
ControlPoint {
id: p3
cx: 600
cy: 1100
}
ControlPoint {
id: c3
color: "blue"
cx: 100
cy: 1100
}
ControlPoint {
id: p4
cx: 100
cy: 600
}
ControlPoint {
id: p5
cx: 600
cy: 330
}
}

View File

@ -0,0 +1,91 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
import QtQuick.Controls
import QtQuick.Dialogs
import io.qt
Item {
id: topLevel
property var boundingRect: Qt.rect(0, 0, 100, 100)
Text {
id: fileNameLabel
x: boundingRect.x
y: boundingRect.bottom * controlPanel.scale + font.pixelSize*2
text: "Filename: " + pathLoader.source
}
ToolButton {
anchors.left: fileNameLabel.right
anchors.top: fileNameLabel.top
anchors.leftMargin: 10
text: "..."
onClicked: {
fileDialog.visible = true
}
}
property var children: []
SvgPathLoader {
id: pathLoader
function reload()
{
for (var j = 0; j < children.length; ++j) {
children[j].destroy()
}
children = []
let first = true
let pickOne = controlPanel.subShape
if (pickOne < 0)
console.debug("Creating " + pathLoader.paths.length + " SVG items")
else
console.log("Creating SVG item", pickOne, "out of", pathLoader.paths.length)
for (var i = 0; i < pathLoader.paths.length; ++i) {
if (pickOne >= 0 && pickOne !== i)
continue
var s = pathLoader.paths[i]
var fillColor = pathLoader.fillColors[i]
var obj = Qt.createQmlObject("import QtQuick\nimport QtQuick.Shapes\n ControlledShape { fillColor: \"" + fillColor + "\"; fillRule: ShapePath.WindingFill; delegate: [ PathSvg { path: \"" + s + "\"; } ] }", topLevel, "SvgPathComponent_" + i)
children.push(obj)
if (first) {
topLevel.boundingRect = obj.boundingRect
first = false
} else {
var minX = Math.min(topLevel.boundingRect.x, obj.boundingRect.x)
var minY = Math.min(topLevel.boundingRect.y, obj.boundingRect.y)
var maxX = Math.max(topLevel.boundingRect.x + topLevel.boundingRect.width,
obj.boundingRect.x + obj.boundingRect.width)
var maxY = Math.max(topLevel.boundingRect.y + topLevel.boundingRect.height,
obj.boundingRect.y + obj.boundingRect.height)
topLevel.boundingRect = Qt.rect(minX, minY, maxX - minX, maxY - minY)
}
}
console.debug("Finished SVG")
}
onPathsChanged: reload()
Component.onCompleted: {
pathLoader.source = "qrc:/1535737773.svg"
}
}
Connections {
target: controlPanel
function onSubShapeChanged() { pathLoader.reload() }
}
FileDialog {
id: fileDialog
title: "Please choose a file"
onAccepted: {
pathLoader.source = fileDialog.selectedFile
fileDialog.visible = false
}
}
}

View File

@ -0,0 +1,31 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
import QtQuick.Controls
import QtQuick.Dialogs
ControlledShape {
fillRule: ShapePath.OddEvenFill
delegate: [
PathText {
text: "foobar"
font: fontDialog.selectedFont
}
]
FontDialog {
id: fontDialog
currentFont.family: "Graziano"
currentFont.pixelSize: 500
}
Button {
anchors.top: parent.bottom
anchors.left: parent.left
text: "Select font"
onClicked: fontDialog.open()
}
}

View File

@ -0,0 +1,37 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
Item {
Repeater {
anchors.fill: parent
model: 2
delegate: ControlledShape {
id: delegate
required property int index
anchors.fill: parent
fillColor: "transparent"
strokeColor: delegate.index === 0 ? "red" : "blue"
strokeStyle: ShapePath.DashLine
strokeWidth: 4
startX: 4
startY: 4
delegate: [
PathArc {
id: arc
x: 96
y: 96
radiusX: 100
radiusY: 100
direction: delegate.index === 0 ? PathArc.Clockwise : PathArc.Counterclockwise
}
]
}
}
}

View File

@ -0,0 +1,72 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
Item {
Repeater {
anchors.fill: parent
model: 2
delegate: ControlledShape {
id: delegate1
required property int index
fillColor: "transparent"
strokeColor: delegate1.index === 0 ? "red" : "blue"
strokeStyle: ShapePath.DashLine
strokeWidth: 4
width: 200
height: 200
anchors.centerIn: parent
startX: 50
startY: 100
delegate: [
PathArc {
x: 150
y: 100
radiusX: 50
radiusY: 20
xAxisRotation: delegate1.index === 0 ? 0 : 45
}
]
}
}
Repeater {
anchors.fill: parent
model: 2
delegate: ControlledShape {
id: delegate2
required property int index
width: 200
height: 200
anchors.centerIn: parent
fillColor: "transparent"
strokeColor: delegate2.index === 0 ? "red" : "blue"
startX: 50
startY: 100
delegate: [
PathArc {
x: 150
y: 100
radiusX: 50
radiusY: 20
xAxisRotation: delegate2.index === 0 ? 0 : 45
direction: PathArc.Counterclockwise
}
]
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@ -0,0 +1,46 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
ControlledShape {
id: capTest
strokeColor: "green"
strokeWidth: 20
fillColor: "transparent"
property int capStyleIdx: 0
readonly property variant styles: [ ShapePath.FlatCap, ShapePath.SquareCap, ShapePath.RoundCap ]
readonly property variant styleTexts: [ qsTr("FlatCap"), qsTr("SquareCap"), qsTr("RoundCap") ]
capStyle: styles[capStyleIdx]
startX: 40
startY: 30
delegate: [
PathQuad {
x: 50
y: 80
controlX: 0
controlY: 80
},
PathLine {
x: 150
y: 80
},
PathQuad {
x: 160
y: 30
controlX: 200
controlY: 80
}
]
Timer {
interval: 1000
repeat: true
running: true
onTriggered: capTest.capStyleIdx = (capTest.capStyleIdx + 1) % capTest.styles.length
}
}

View File

@ -0,0 +1,103 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
ControlledShape {
id: shape
strokeWidth: 4
strokeColor: "black"
startX: 50
startY: 100
delegate: [
PathCubic {
x: 150
y: 100
control1X: cp1.x
control1Y: cp1.y
control2X: cp2.x
control2Y: cp2.y
}
]
Rectangle {
id: cp1
color: "red"
width: 10
height: 10
SequentialAnimation {
loops: Animation.Infinite
running: true
NumberAnimation {
target: cp1
property: "x"
from: 0
to: flickable.width - cp1.width
duration: 5000
}
NumberAnimation {
target: cp1
property: "x"
from: flickable.width - cp1.width
to: 0
duration: 5000
}
NumberAnimation {
target: cp1
property: "y"
from: 0
to: flickable.height - cp1.height
duration: 5000
}
NumberAnimation {
target: cp1
property: "y"
from: flickable.height - cp1.height
to: 0
duration: 5000
}
}
}
Rectangle {
id: cp2
color: "blue"
width: 10
height: 10
x: flickable.width - width
SequentialAnimation {
loops: Animation.Infinite
running: true
NumberAnimation {
target: cp2
property: "y"
from: 0
to: flickable.height - cp2.height
duration: 5000
}
NumberAnimation {
target: cp2
property: "y"
from: flickable.height - cp2.height
to: 0
duration: 5000
}
NumberAnimation {
target: cp2
property: "x"
from: flickable.width - cp2.width
to: 0
duration: 5000
}
NumberAnimation {
target: cp2
property: "x"
from: 0
to: flickable.width - cp2.width
duration: 5000
}
}
}
}

View File

@ -0,0 +1,36 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
ControlledShape {
id: shape
anchors.fill: parent
strokeWidth: 5
strokeColor: "blue"
strokeStyle: ShapePath.DashLine
//dashPattern: [ 1, 4, 4, 4 ]
fillColor: "lightBlue"
property real xr: 70
property real yr: 30
startX: shape.width / 2 - xr
startY: shape.height / 2 - yr
delegate: [
PathArc {
x: shape.width / 2 + shape.xr
y: shape.height / 2 + shape.yr
radiusX: shape.xr
radiusY: shape.yr
useLargeArc: true
},
PathArc {
x: shape.width / 2 - shape.xr
y: shape.height / 2 - shape.yr
radiusX: shape.xr
radiusY: shape.yr
useLargeArc: true
}
]
}

View File

@ -0,0 +1,170 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "debugpaintitem.h"
#include <QPainter>
#include <QPen>
DebugPaintItem::DebugPaintItem(QQuickItem *item)
: QQuickPaintedItem(item)
{
connect(this, &DebugPaintItem::shapeChanged, this, &QQuickItem::update);
connect(this, &DebugPaintItem::colorChanged, this, &QQuickItem::update);
connect(this, &DebugPaintItem::pathScaleChanged, this, &QQuickItem::update);
connect(this, &DebugPaintItem::fillRuleChanged, this, &QQuickItem::update);
connect(this, &DebugPaintItem::strokeChanged, this, &QQuickItem::update);
}
void DebugPaintItem::setShape(QQuickPath *p)
{
if (p == m_path)
return;
m_path = p;
emit shapeChanged();
}
QQuickPath *DebugPaintItem::shape() const
{
return m_path;
}
void DebugPaintItem::handlePathChanged()
{
update();
}
void DebugPaintItem::paint(QPainter *p)
{
if (!isVisible())
return;
if (m_path != nullptr) {
QPainterPath painterPath = m_path->path();
painterPath.setFillRule(m_fillRule);
p->scale(m_pathScale, m_pathScale);
p->setRenderHint(QPainter::Antialiasing);
if (m_strokeColor.alpha() > 0) {
QPen pen(m_strokeStyle);
pen.setWidthF(m_strokeWidth);
pen.setCapStyle(m_capStyle);
pen.setJoinStyle(m_joinStyle);
pen.setColor(m_strokeColor);
p->setPen(pen);
} else {
p->setPen(Qt::NoPen);
}
p->setBrush(m_color);
p->drawPath(painterPath);
}
}
QColor DebugPaintItem::color() const
{
return m_color;
}
void DebugPaintItem::setColor(const QColor &color)
{
if (m_color == color)
return;
m_color = color;
emit colorChanged();
}
void DebugPaintItem::setPathScale(qreal pathScale)
{
if (qFuzzyCompare(m_pathScale, pathScale))
return;
m_pathScale = pathScale;
emit pathScaleChanged();
}
qreal DebugPaintItem::pathScale() const
{
return m_pathScale;
}
void DebugPaintItem::setFillRule(Qt::FillRule fillRule)
{
if (m_fillRule == fillRule)
return;
m_fillRule = fillRule;
emit fillRuleChanged();
}
Qt::FillRule DebugPaintItem::fillRule() const
{
return m_fillRule;
}
void DebugPaintItem::setStrokeColor(const QColor &strokeColor)
{
if (m_strokeColor == strokeColor)
return;
m_strokeColor = strokeColor;
emit strokeChanged();
}
QColor DebugPaintItem::strokeColor() const
{
return m_strokeColor;
}
void DebugPaintItem::setStrokeStyle(Qt::PenStyle strokeStyle)
{
if (m_strokeStyle == strokeStyle)
return;
m_strokeStyle = strokeStyle;
emit strokeChanged();
}
Qt::PenStyle DebugPaintItem::strokeStyle() const
{
return m_strokeStyle;
}
void DebugPaintItem::setJoinStyle(Qt::PenJoinStyle style)
{
if (m_joinStyle == style)
return;
m_joinStyle = style;
emit strokeChanged();
}
Qt::PenJoinStyle DebugPaintItem::joinStyle() const
{
return m_joinStyle;
}
void DebugPaintItem::setCapStyle(Qt::PenCapStyle style)
{
if (m_capStyle == style)
return;
m_capStyle = style;
emit strokeChanged();
}
Qt::PenCapStyle DebugPaintItem::capStyle() const
{
return m_capStyle;
}
void DebugPaintItem::setStrokeWidth(qreal w)
{
if (qFuzzyCompare(m_strokeWidth, w))
return;
m_strokeWidth = w;
emit strokeChanged();
}
qreal DebugPaintItem::strokeWidth() const
{
return m_strokeWidth;
}

View File

@ -0,0 +1,79 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef DEBUGPAINTITEM_H
#define DEBUGPAINTITEM_H
#include <QQuickPaintedItem>
#include <QtQuick/private/qquickpath_p.h>
class DebugPaintItem : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(QQuickPath *shape READ shape WRITE setShape NOTIFY shapeChanged)
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
Q_PROPERTY(qreal pathScale READ pathScale WRITE setPathScale NOTIFY pathScaleChanged)
Q_PROPERTY(Qt::FillRule fillRule READ fillRule WRITE setFillRule NOTIFY fillRuleChanged)
Q_PROPERTY(QColor strokeColor READ strokeColor WRITE setStrokeColor NOTIFY strokeChanged)
Q_PROPERTY(Qt::PenStyle strokeStyle READ strokeStyle WRITE setStrokeStyle NOTIFY strokeChanged)
Q_PROPERTY(Qt::PenJoinStyle joinStyle READ joinStyle WRITE setJoinStyle NOTIFY strokeChanged)
Q_PROPERTY(Qt::PenCapStyle capStyle READ capStyle WRITE setCapStyle NOTIFY strokeChanged)
Q_PROPERTY(qreal strokeWidth READ strokeWidth WRITE setStrokeWidth NOTIFY strokeChanged)
public:
DebugPaintItem(QQuickItem *item = nullptr);
void setShape(QQuickPath *path);
QQuickPath *shape() const;
void setColor(const QColor &color);
QColor color() const;
void setPathScale(qreal pathScale);
qreal pathScale() const;
void setFillRule(Qt::FillRule filleRule);
Qt::FillRule fillRule() const;
QColor strokeColor() const;
void setStrokeColor(const QColor &strokeColor);
Qt::PenStyle strokeStyle() const;
void setStrokeStyle(Qt::PenStyle penStyle);
Qt::PenJoinStyle joinStyle() const;
void setJoinStyle(Qt::PenJoinStyle style);
Qt::PenCapStyle capStyle() const;
void setCapStyle(Qt::PenCapStyle style);
qreal strokeWidth() const;
void setStrokeWidth(qreal w);
signals:
void shapeChanged();
void colorChanged();
void opacityChanged();
void pathScaleChanged();
void fillRuleChanged();
void strokeChanged();
public slots:
void handlePathChanged();
protected:
void paint(QPainter *p) override;
private:
QQuickPath *m_path = nullptr;
QColor m_color = Qt::red;
qreal m_pathScale = 1.0;
Qt::FillRule m_fillRule = Qt::WindingFill;
Qt::PenStyle m_strokeStyle = Qt::NoPen;
QColor m_strokeColor = Qt::transparent;
qreal m_strokeWidth = 1.0;
Qt::PenCapStyle m_capStyle = Qt::FlatCap;
Qt::PenJoinStyle m_joinStyle = Qt::MiterJoin;
};
#endif // DEBUGPAINTITEM_H

View File

@ -0,0 +1,46 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "debugvisualizationcontroller.h"
#include <private/qquickshapecurverenderer_p.h>
DebugVisualizationController::DebugVisualizationController(QObject *parent)
: QObject{parent}
{
}
bool DebugVisualizationController::showCurves() const
{
return m_showCurves;
}
void DebugVisualizationController::setShowCurves(bool newShowCurves)
{
if (m_showCurves == newShowCurves)
return;
m_showCurves = newShowCurves;
update();
emit showCurvesChanged();
}
bool DebugVisualizationController::showWireframe() const
{
return m_showWireframe;
}
void DebugVisualizationController::setWireframe(bool newShowWireframe)
{
if (m_showWireframe == newShowWireframe)
return;
m_showWireframe = newShowWireframe;
update();
emit showWireframeChanged();
}
void DebugVisualizationController::update()
{
int flags = (m_showCurves ? QQuickShapeCurveRenderer::DebugCurves : 0)
| (m_showWireframe ? QQuickShapeCurveRenderer::DebugWireframe : 0);
QQuickShapeCurveRenderer::setDebugVisualization(flags);
emit settingsChanged();
}

View File

@ -0,0 +1,35 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef DEBUGVISUALIZATIONCONTROLLER_H
#define DEBUGVISUALIZATIONCONTROLLER_H
#include <QObject>
class DebugVisualizationController : public QObject
{
Q_OBJECT
Q_PROPERTY(bool showCurves READ showCurves WRITE setShowCurves NOTIFY showCurvesChanged)
Q_PROPERTY(bool showWireframe READ showWireframe WRITE setWireframe NOTIFY showWireframeChanged)
public:
explicit DebugVisualizationController(QObject *parent = nullptr);
bool showCurves() const;
void setShowCurves(bool newShowCurves);
bool showWireframe() const;
void setWireframe(bool newShowWireframe);
signals:
void showCurvesChanged();
void showWireframeChanged();
void settingsChanged();
private:
void update();
bool m_showCurves = false;
bool m_showWireframe = false;
};
#endif // DEBUGVISUALIZATIONCONTROLLER_H

View File

@ -0,0 +1,77 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
Item {
ControlledShape {
id: shape
anchors.top: parent.top
anchors.left: parent.left
width: parent.width
height: parent.height / 2
startX: 10
startY: 100
delegate: [
PathArc {
relativeX: 50
y: 100
radiusX: 25
radiusY: 25
},
PathArc {
relativeX: 50
y: 100
radiusX: 25
radiusY: 35
},
PathArc {
relativeX: 50
y: 100
radiusX: 25
radiusY: 60
},
PathArc {
relativeX: 50
y: 100
radiusX: 50
radiusY: 120
}
]
}
ControlledShape {
width: parent.width
height: parent.height / 2
anchors.bottom: parent.bottom
anchors.right: parent.right
fillColor: "transparent"
strokeColor: "darkBlue"
strokeWidth: 20
capStyle: ShapePath.RoundCap
delegate: [
PathAngleArc {
centerX: 65
centerY: 95
radiusX: 45
radiusY: 45
startAngle: -180
SequentialAnimation on sweepAngle {
loops: Animation.Infinite
NumberAnimation {
to: 360
duration: 2000
}
NumberAnimation {
to: 0
duration: 2000
}
}
}
]
}
}

View File

@ -0,0 +1,44 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
ControlledShape {
id: star
strokeColor: "blue"
fillColor: "magenta"
strokeWidth: 2
delegate: [
PathMove {
x: 90
y: 50
},
PathLine {
x: 50 + 40 * Math.cos(0.8 * 1 * Math.PI)
y: 50 + 40 * Math.sin(0.8 * 1 * Math.PI)
},
PathLine {
x: 50 + 40 * Math.cos(0.8 * 2 * Math.PI)
y: 50 + 40 * Math.sin(0.8 * 2 * Math.PI)
},
PathLine {
x: 50 + 40 * Math.cos(0.8 * 3 * Math.PI)
y: 50 + 40 * Math.sin(0.8 * 3 * Math.PI)
},
PathLine {
x: 50 + 40 * Math.cos(0.8 * 4 * Math.PI)
y: 50 + 40 * Math.sin(0.8 * 4 * Math.PI)
},
PathLine {
x: 90
y: 50
}
]
Timer {
interval: 2000
onTriggered: star.fillRule = (star.fillRule === ShapePath.OddEvenFill ? ShapePath.WindingFill : ShapePath.OddEvenFill)
repeat: true
running: true
}
}

View File

@ -0,0 +1,72 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
Item {
ControlledShape {
anchors.fill: parent
strokeColor: "transparent"
startX: 10
startY: 10
delegate: [
PathLine {
relativeX: 180
relativeY: 0
},
PathLine {
relativeX: 0
relativeY: 180
},
PathLine {
relativeX: -180
relativeY: 0
},
PathLine {
relativeX: 0
relativeY: -180
}
]
}
Timer {
id: spreadTimer
interval: 3000
running: true
repeat: true
readonly property variant spreads: [ ShapeGradient.PadSpread, ShapeGradient.RepeatSpread, ShapeGradient.ReflectSpread ]
readonly property variant spreadTexts: [ qsTr("PadSpread"), qsTr("RepeatSpread"), qsTr("ReflectSpread") ]
property int spreadIdx: 0
onTriggered: function() {
spreadIdx = (spreadIdx + 1) % spreads.length
grad.spread = spreads[spreadIdx]
}
}
ControlledShape {
anchors.fill: parent
strokeColor: "gray"
strokeWidth: 2
fillColor: "transparent"
delegate: [
PathMove {
x: 0
y: 50
},
PathLine {
relativeX: 200
relativeY: 0
},
PathMove {
x: 0
y: 150
},
PathLine {
relativeX: 200
relativeY: 0
}
]
}
}

View File

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:ns1="http://sozi.baierouge.fr"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
id="svg10114"
sodipodi:docname="New document 114"
viewBox="0 0 210.55 207.21"
version="1.1"
inkscape:version="0.48.4 r9939"
>
<sodipodi:namedview
id="base"
bordercolor="#666666"
inkscape:pageshadow="2"
inkscape:window-y="170"
fit-margin-left="0"
pagecolor="#ffffff"
fit-margin-top="0"
inkscape:window-maximized="0"
inkscape:zoom="0.35"
inkscape:window-x="381"
inkscape:window-height="455"
showgrid="false"
borderopacity="1.0"
inkscape:current-layer="layer1"
inkscape:cx="177.4157"
inkscape:cy="37.888957"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="515"
inkscape:pageopacity="0.0"
inkscape:document-units="px"
/>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(-197.58 -363.04)"
>
<path
id="path10106"
sodipodi:nodetypes="sssssssscssssssssssssssssssssssssssssscssssssssscsssssssssssassssssssssssssscscccssssssssssssssssscacasssssssssssssssssssscscc"
style="fill:#000000"
inkscape:connector-curvature="0"
d="m322.61 371.62c-1.5377 0.0333-3.5271 0.80745-6.0938 2.25-4.9988 2.8095-6.3561 7.2849-5.4688 18.062 0.51964 6.3116 0.32732 8.239-1.0312 10.312-1.356 2.0695-1.5585 4.005-1.0625 10.438 0.33561 4.3521 1.1508 8.7842 1.8125 9.8438 0.96765 1.5495 0.87327 2.6094-0.46875 5.4375-2.049 4.3179-2.0715 5.7903-0.1875 8.2812 0.8075 1.0676 1.4825 2.5854 1.5 3.375 0.0175 0.78955 0.99163 2.477 2.1875 3.75 2.608 2.7761 3.0737 2.8262 5.0312 0.5625 3.1122-3.5987 3.9502-8.9023 4.375-28.031 0.31534-14.199 0.80913-20.033 1.875-22.094 0.80879-1.564 1.5884-6.3682 1.75-10.844 0.28534-7.9048-0.83575-11.417-4.2188-11.344zm-53 14.062c-1.9081 0-6.859 2.9524-7.8125 4.6562-1.1433 2.0429-0.39376 13.788 1.125 17.844 0.61791 1.65 1.9625 3.6232 3 4.375 1.1193 0.81114 1.6944 2.1169 1.4062 3.2188-0.69123 2.6432 0.97813 9.3176 3.8438 15.375 1.3522 2.8583 2.9145 7.6087 3.4688 10.531 0.55425 2.9226 2.2388 7.2762 3.75 9.6875 2.3396 3.7331 3.1564 4.3347 5.625 4.0938 3.7598-0.36694 5.5441-3.0273 3.8438-5.75-0.68348-1.0944-1.2188-3.1291-1.2188-4.5s-1.1024-5.7509-2.4375-9.75-2.4246-9.0812-2.4062-11.281c0.0184-2.2-0.46367-6.25-1.0938-9-1.0663-4.6539-4.014-21.112-4.0625-22.719-0.0353-1.1676-5.8553-6.7812-7.0312-6.7812zm87.719 2.1875c-3.4842-0.0817-7.1481 2.6491-8.6562 7.2188-1.0035 3.0406-2.0408 14.629-2.0938 23.594-0.0114 1.925-0.49233 5.0413-1.0938 6.9062-0.60142 1.8649-1.0482 5.3488-1 7.75 0.07 3.4862-0.5085 5.0547-2.875 7.75-3.4871 3.9716-4.1106 9.8469-1.2188 11.531 0.9625 0.56061 2.261 1.0146 2.9062 1.0312 2.0704 0.0537 7.3438-5.081 7.3438-7.1562 0-1.1038 0.64287-3.1964 1.4375-4.6562 2.6446-4.8585 7.6275-23.031 7-25.531-0.35831-1.4276 0.1021-3.7719 1.125-5.75 0.9498-1.8367 1.8627-4.9087 2.0312-6.8438 0.16858-1.935 0.60352-5.3826 0.96875-7.6562 0.54958-3.4212 0.28253-4.4987-1.4688-6.25-1.2836-1.2836-2.8225-1.9004-4.4062-1.9375zm38.962 39.776c-0.41087-1.3177-2.649-3.1827-2.649-3.1827-5.6222 0.62958-7.1736 1.4539-9.9688 5.3125-2.033 2.8064-3.124 5.767-3.5938 9.7188-0.50455 4.2441-1.3633 6.4493-3.4375 8.6875-1.5291 1.65-3.8228 5.0269-5.0625 7.5s-2.9062 4.9996-3.7188 5.5938c-1.7406 1.2728-6.1921 14.341-5.5 16.156 0.90097 2.3631 4.0452 1.26 7.4062-2.625 1.8515-2.1402 4.4737-5.1777 5.8438-6.75 4.0598-4.659 11.031-16.282 11.031-18.406 0-0.63241 0.9-2.2891 2-3.6875s2-3.3296 2-4.2812c0-2.1982 3.7305-4.6335 4.742-7.3758 0.77537-2.1021 1.6609-4.2424 0.90707-6.6602zm-107.4 33.88c-2.7007-0.0514-4.8692 0.33799-5.7812 1.25-0.9007 0.9007-1.6649 4.5217-2.0312 9.5938-0.61163 8.4673 0.68391 13.233 4.375 15.938 1.3221 0.96856 1.125 1.6325-1.625 4.9688-1.7472 2.1197-3.6988 1.3702-5.4885 2.2118-2.7058 1.2724-4.2526 5.347-4.5115 8.4758-0.19436 2.3492-1.2274 4.6649-2.75 6.1875-1.3413 1.3413-2.4375 3.5424-2.4375 4.875 0 1.3327-0.68534 2.9801-1.5 3.6562-1.5011 1.2458-7.4902 0.99608-10.75-0.4375-2.0861-0.91741-3.75 2.6725-3.75 8.0312 0 2.7703 1.0145 4.5375 5.2812 9.1562 17.679 19.138 24.149 24.25 30.625 24.25 1.6394 0 3.2137 0.42371 3.5312 0.9375 0.31753 0.51376 2.0351 0.73192 3.8125 0.46875 5.7811-0.85595 23.001-2.3031 33.219-2.7812l10-0.46875 3.5-3.9375c1.934-2.1628 5.0241-6.3457 6.8438-9.3125 2.7166-4.4293 3.2194-6.1294 2.8125-9.4062-0.34558-2.7831-0.0335-4.5077 1.0625-5.7188 1.1973-1.323 1.6274-4.4114 1.8125-13 0.16512-7.6629 0.77195-12.524 1.875-15.156 0.89247-2.1297 1.6388-4.8297 1.6562-6 0.0175-1.1703 0.6925-2.9949 1.5-4.0625 3.3493-4.4282 0.60788-8.5625-5.6875-8.5625-3.1563 0-4.9116-0.75392-8.1562-3.5312-2.2727-1.9453-5.2665-3.8195-6.6562-4.1562-1.3898-0.33673-3.4151-1.2258-4.5-1.9688-1.0849-0.74294-2.614-1.3438-3.375-1.3438-0.76098 0-3.0271-1.2925-5.0312-2.875-4.3642-3.446-12.675-4.9864-17.094-3.1562-2.3602 0.97763-3.4086 0.8936-6.1875-0.5625-4.1163-2.1568-10.093-3.4769-14.594-3.5625zm-73.375 17.938c-1.3882-0.0661-2.5361 0.22802-4.2812 0.84375-2.1608 0.7624-2.8143 1.7251-3.6496 3.0802-0.96454 1.5646-1.225 3.8922-1.2843 5.3625-0.0716 1.7769 0.60748 3.6445 1.6214 5.1199 2.4979 3.6347 4.7532 2.6468 10.344 8.25 4.7079 4.7186 9.3065 8.5625 10.25 8.5625 0.94347 0 3.2613 1.125 5.125 2.5 4.0233 2.9683 6.95 3.1826 10.031 0.6875 2.9687-2.4039 2.1009-6.0679-2-8.375-2.5053-1.4095-3.0312-2.3588-3.0312-5.375 0-8.2634-7.1413-16.183-17.438-19.344-2.671-0.81994-4.2994-1.2464-5.6875-1.3125zm87.781 15.844c0.43126 0.007 0.89972 0.14969 1.4375 0.4375 2.3391 1.2518 14.915 1.6771 16.719 0.5625 2.3457-1.4497 2.6432 0.3729 0.65625 4.0625-1.5014 2.7879-1.9615 5.6783-2 12.719-0.0274 5.0078-0.38695 9.6562-0.8125 10.344-0.92832 1.4998-3.2958 1.6422-4.1562 0.25-0.33992-0.55-1.9394-1-3.5312-1-1.9787 0-3.4592-0.80091-4.6875-2.5625-0.98817-1.4172-3.0717-3.9871-4.6562-5.6875-2.7528-2.9542-2.8667-3.3899-2.3125-10 0.54096-6.4526 1.475-9.157 3.3438-9.125zm94.438 65.938c0.24133 0.40465 0.5482 0.4375 0.90625 0.4375 0.31607 0 0.55718-0.0265 0.78125-0.4375z"
/>
</g
>
<metadata
>
<rdf:RDF
>
<cc:Work
>
<dc:format
>image/svg+xml</dc:format
>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage"
/>
<cc:license
rdf:resource="http://creativecommons.org/licenses/publicdomain/"
/>
<dc:publisher
>
<cc:Agent
rdf:about="http://openclipart.org/"
>
<dc:title
>Openclipart</dc:title
>
</cc:Agent
>
</dc:publisher
>
</cc:Work
>
<cc:License
rdf:about="http://creativecommons.org/licenses/publicdomain/"
>
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction"
/>
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution"
/>
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks"
/>
</cc:License
>
</rdf:RDF
>
</metadata
>
</svg
>

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

@ -0,0 +1,40 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
ControlledShape {
strokeColor: "black"
strokeWidth: 16
fillColor: "transparent"
capStyle: ShapePath.RoundCap
//readonly property variant styles: [ ShapePath.BevelJoin, ShapePath.MiterJoin, ShapePath.RoundJoin ]
//joinStyle: styles[joinStyleIdx]
property int joinStyleIdx: 0
readonly property variant styles: [ ShapePath.BevelJoin, ShapePath.MiterJoin, ShapePath.RoundJoin ]
readonly property variant styleTexts: [ qsTr("BevelJoin"), qsTr("MiterJoin"), qsTr("RoundJoin") ]
startX: 30
startY: 30
delegate: [
PathLine {
x: 100
y: 100
},
PathLine {
x: 30
y: 100
}
]
// Timer {
// interval: 1000
// repeat: true
// running: true
// onTriggered: joinTest.joinStyleIdx = (joinTest.joinStyleIdx + 1) % joinTest.styles.length
// }
}

View File

@ -0,0 +1,34 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
Item {
Repeater {
anchors.fill: parent
model: 2
delegate: ControlledShape {
id: delegate
required property int index
anchors.fill: parent
fillColor: "transparent"
strokeColor: delegate.index === 0 ? "red" : "blue"
strokeStyle: ShapePath.DashLine
strokeWidth: 4
startX: 50
startY: 100
delegate: [
PathArc {
x: 100
y: 150
radiusX: 50
radiusY: 50
useLargeArc: delegate.index === 1
}
]
}
}
}

View File

@ -0,0 +1,84 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
ControlledShape {
strokeWidth: 4
strokeColor: "red"
// fillGradient: LinearGradient {
// x1: 20
// y1: 20
// x2: 180
// y2: 130
// 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"
// }
// }
fillColor: "blue" // ignored with the gradient set
strokeStyle: ShapePath.DashLine
// dashPattern: [ 1, 4 ]
startX: 20
startY: 20
delegate: [
PathLine {
x: 180
y: 130
},
PathLine {
x: 20
y: 130
},
PathLine {
x: 20
y: 20
}
]
// transform: Rotation {
// origin.x: 100
// origin.y: 50
// axis {
// x: 0
// y: 1
// z: 0
// }
// SequentialAnimation on angle {
// NumberAnimation {
// from: 0
// to: 75
// duration: 2000
// }
// NumberAnimation {
// from: 75
// to: -75
// duration: 4000
// }
// NumberAnimation {
// from: -75
// to: 0
// duration: 2000
// }
// loops: Animation.Infinite
// }
// }
}

View File

@ -0,0 +1,34 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QFontDatabase>
#include "svgpathloader.h"
#include "debugpaintitem.h"
#include "debugvisualizationcontroller.h"
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
qmlRegisterType<DebugPaintItem>("io.qt", 1, 0, "DebugPaintItem");
qmlRegisterType<SvgPathLoader>("io.qt", 1, 0, "SvgPathLoader");
qmlRegisterType<DebugVisualizationController>("io.qt", 1, 0, "DebugVisualizationController");
QFontDatabase::addApplicationFont(":/Graziano.ttf");
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}

View File

@ -0,0 +1,189 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Window
import QtQuick.Shapes
import QtQuick.Controls
import QtQuick.Layouts
Window {
width: 1024
height: 768
visible: true
title: qsTr("Hello World")
color: "#666"
ListModel {
id: sampleList
ListElement {
text: "Small polygon"
source: "SmallPolygon.qml"
}
ListElement {
text: "Text"
source: "TextShape.qml"
}
ListElement {
text: "SVG"
source: "SvgShape.qml"
}
ListElement {
text: "QuadShape"
source: "SimpleShape.qml"
}
ListElement {
text: "Squircle"
source: "Squircle.qml"
}
ListElement {
text: "CubicShape"
source: "CubicShape.qml"
}
ListElement {
text: "Arc Direction"
source: "arcDirection.qml"
}
ListElement {
text: "Arc Rotation"
source: "arcRotation.qml"
}
ListElement {
text: "Cap Styles"
source: "capStyles.qml"
}
ListElement {
text: "Cubic Curve"
source: "cubicCurve.qml"
}
ListElement {
text: "Dash Pattern"
source: "dashPattern.qml"
}
ListElement {
text: "Elliptical Arcs"
source: "ellipticalArcs.qml"
}
ListElement {
text: "Fill rules"
source: "fillRules.qml"
}
ListElement {
text: "Gradient spread modes"
source: "gradientSpreadModes.qml"
}
ListElement {
text: "Join styles"
source: "joinStyles.qml"
}
ListElement {
text: "Large or small arc"
source: "largeOrSmallArc.qml"
}
ListElement {
text: "Linear gradient"
source: "linearGradient.qml"
}
ListElement {
text: "Quadratic curve"
source: "quadraticCurve.qml"
}
ListElement {
text: "Radial gradient"
source: "radialGradient.qml"
}
ListElement {
text: "Stroke or fill"
source: "strokeOrFill.qml"
}
ListElement {
text: "Qt! text"
source: "text.qml"
}
ListElement {
text: "Tiger"
source: "tiger.qml"
}
}
ComboBox {
id: comboBox
model: sampleList
textRole: "text"
valueRole: "source"
onCurrentValueChanged: {
loader.source = currentValue
}
}
Image {
id: background
anchors.fill: flickable
fillMode: Image.Tile
source: "qrc:/background.png"
smooth: true
}
Flickable {
id: flickable
clip: true
contentWidth: loader.item ? loader.item.boundingRect.right * controlPanel.scale + controlPanel.pathMargin * 2 : 1
contentHeight: loader.item ? loader.item.boundingRect.bottom * controlPanel.scale + controlPanel.pathMargin * 2 : 1
anchors.top: comboBox.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: controlPanel.top
WheelHandler {
onWheel: (event)=> {
let scale = controlPanel.scale
let posX = event.x
let posY = event.y
let xOff = posX - flickable.contentX
let yOff = posY - flickable.contentY
let pathX = posX / scale
let pathY = posY / scale
if (event.angleDelta.y > 0)
scale = scale * 1.1
else
scale = scale / 1.1
controlPanel.setScale(scale)
flickable.contentX = pathX * controlPanel.scale - xOff
flickable.contentY = pathY * controlPanel.scale - yOff
flickable.returnToBounds()
}
}
Loader {
x: controlPanel.pathMargin
y: controlPanel.pathMargin
id: loader
}
}
ControlPanel {
id: controlPanel
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
height: parent.height / 4
}
}

View File

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
width="330.31769"
height="652.32654"
sodipodi:docname="166413_160676353985218_105678756151645_360714_7013010_n.jpg">
<metadata
id="metadata8">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs6" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1280"
inkscape:window-height="749"
id="namedview4"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="0.76005001"
inkscape:cx="162.4278"
inkscape:cy="342.07912"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<g
id="g3001"
transform="translate(-120.24391,40.527705)">
<path
sodipodi:nodetypes="ssssssssczsssssss"
inkscape:connector-curvature="0"
id="path2999"
d="m 333.68126,543.99038 c -6.91753,-4.38697 -11.36161,-10.23632 -13.34555,-17.56558 -1.93876,-7.16237 -17.29569,-22.30618 -25.49276,-31.28027 -5.66358,-6.20045 -10.53284,-9.06593 -4.03049,-16.38723 5.60876,-6.31517 11.66711,-27.57804 12.39492,-33.72331 0.89475,-7.55473 19.88838,13.07827 29.55699,12.32647 15.07492,-1.17218 19.87293,-8.57979 31.333,-21.95526 9.95433,-11.61808 15.38922,-24.72893 21.56477,-35.66576 3.10302,-5.49543 14.54668,-28.68741 15.54455,-27.78873 2.65231,2.82162 7.60302,15.21755 8.9266,25.36832 1.32359,10.15076 1.33221,24.47455 5.4914,37.36519 3.70609,11.48633 2.22562,32.47582 -3.24615,46.02256 -2.19534,5.43511 -13.41276,12.171 -28.30712,34.76544 -2.04972,3.1094 -5.63972,6.63261 -9.09235,8.92323 -3.1379,2.08181 -9.31989,7.24499 -13.73775,11.47372 -4.41786,4.22873 -9.45802,8.42578 -11.20034,9.32677 -4.95722,2.56348 -11.13434,2.10828 -16.35972,-1.20556 z"
style="fill:#000000" />
<path
sodipodi:nodetypes="ssssssssssssssssscssss"
inkscape:connector-curvature="0"
id="path2997"
d="m 236.5,478.28799 c -26.46888,-1.7341 -59.59471,-6.73425 -65.76997,-9.9276 -3.78272,-1.95612 -8.3648,-9.36928 -9.82546,-15.89622 -1.67608,-7.48953 -0.60289,-28.26342 1.85793,-35.96417 1.05453,-3.3 4.9189,-13.03322 8.58748,-21.62937 C 179.00832,376.92572 182,372.28356 182,363.21743 c 0,-3.50469 0.61152,-7.83574 1.35894,-9.62455 1.49509,-3.57827 2.36599,-9.42794 23.40534,-17.67708 15.33826,-6.01386 25.16813,-11.90646 32.74071,-18.37591 12.3733,-10.57084 26.6062,-13.06992 48.57877,-7.38876 17.54428,4.5362 25.34686,7.84887 38.9754,7.84887 6.08978,0 17.19611,-0.85492 18.24842,-0.29174 1.11527,0.59687 1.27225,5.31825 2.21511,8.56158 1.33843,4.60404 1.41526,7.68415 0.44529,11.54174 -2.05134,8.15817 -9.32024,15.59461 -13.88659,17.61453 -5.06052,2.23851 -16.40257,4.51284 -31.25983,5.67633 -14.7923,1.15841 -19.21657,-0.79634 -27.99177,15.58765 l -3.31538,6.19009 10.91268,15.36198 c 11.78018,16.58318 17.44498,48.05873 11.58963,61.65598 -3.10121,7.2016 -8.24447,17.64394 -14.43161,22.20637 -9.7269,7.17267 -28.89257,-2.88671 -43.08511,-3.81652 z"
style="fill:#000000" />
<path
sodipodi:nodetypes="ssaaaasscss"
inkscape:connector-curvature="0"
id="path2995"
d="m 317.68552,447.77108 c -4.67916,-1.18677 -9.1372,-7.95998 -9.65154,-12.98136 -0.48763,-4.76058 3.66294,-14.83262 6.92258,-21.68422 4.49604,-9.45044 11.55001,-17.4567 17.1823,-26.27727 5.4084,-8.46995 10.65679,-17.0411 15.94923,-25.58399 7.17401,-11.58005 7.84367,-33.73833 21.42439,-34.80044 14.18977,-1.10974 28.2626,27.80345 27.76586,32.43896 -1.92945,18.00547 -12.74615,35.2835 -22.17876,51.23197 -6.61784,11.18931 -11.77374,15.63521 -18.1815,27.31116 -4.3386,7.809 -9.17117,10.7187 -17.04193,15.96767 -8.33187,5.55649 -19.11228,-4.84172 -22.19063,-5.62248 z"
style="fill:#000000" />
<path
sodipodi:nodetypes="csssassaasc"
inkscape:connector-curvature="0"
id="path2993"
d="m 297.7121,397.62093 c -4.37978,-0.76378 -11.1662,-5.35567 -13.43054,-9.08751 -1.05408,-1.73722 -5.27599,-4.42533 -4.52995,-6.66133 3.79539,-11.37539 8.14861,-15.05527 11.11936,-17.01179 1.47519,-0.97155 10.91285,-2.35405 16.45476,-2.37949 5.35694,-0.0246 14.398,2.6739 15.926,2.15393 6.39248,-2.1753 21.58665,-11.63284 16.66639,-3.95961 -1.74425,2.72018 -9.86689,20.36174 -15.66412,30.05992 -2.80151,4.68665 -3.99996,11.91427 -9.21074,13.54551 -5.90623,1.84894 -15.24781,-6.29628 -17.33116,-6.65959 z"
style="fill:#000000" />
<path
sodipodi:nodetypes="ssssssssssccssssccsssscssccssssscss"
inkscape:connector-curvature="0"
id="path2991"
d="m 189,334.76618 c -3.56886,-1.87317 -3.73828,-7.15428 -0.55687,-17.35819 5.01517,-16.0854 8.92638,-21.55121 8.92639,-47.57388 2e-5,-23.36514 -3.95828,-35.38789 -7.18098,-47.31903 -1.78273,-6.6 -3.77628,-16.86508 -6.83769,-24.01508 -8.99294,-21.00325 -12.2685,-31.92583 -13.8777,-46.27612 C 168.53694,143.87507 165.62544,129.70037 161.00571,111 152.60865,77.00927 151.53855,67.780331 154.95103,58.78242 158.45942,49.531613 167.63712,43 177.12722,43 c 5.76576,0 14.29179,3.435547 18.43755,7.429376 4.91773,4.73752 12.26688,9.449135 15.70129,28.754921 2.71875,14.37298 5.5615,30.709973 7.68228,44.315703 2.82481,18.38886 4.9316,24.88951 8.2153,32.19284 5.77357,12.84108 9.22986,29.46676 11.71356,50.30716 0.55716,4.675 2.6273,13.9 4.60031,20.5 1.97301,6.6 4.35732,12.20086 5.75964,13.99194 4.31214,5.50757 4.20182,11.46411 9.8041,16.33937 4.89284,3.932 5.17553,-4.93168 8.48229,-8.86127 5.04933,-6.00079 4.34525,-17.53657 9.98389,-34.12517 6.53313,-19.22016 5.46933,-38.15405 19.40326,-68.32952 12.90365,-27.94429 9.48745,-39.81066 16.06865,-59.54708 6.06973,-18.202577 7.65173,-41.314931 21.79742,-54.279307 6.74694,-6.183506 18.29831,-8.217335 26.58616,-6.854856 8.28785,1.362479 17.87216,3.615793 19.20995,12.536783 0.8831,5.888923 2.70582,18.266918 -3.21886,39.099813 -6.49981,22.855208 -13.44316,35.388777 -16.01082,52.270797 -6.1548,20.88948 -8.08415,24.9957 -13.41238,50.5742 -4.92944,22.91145 -9.053,30.98714 -15.44314,44.38602 -4.78823,10.03997 -5.29352,16.18349 -7.50248,25.34215 -1.64534,6.82178 -4.51529,18.70324 -6.37768,26.40324 -3.3461,13.83435 -11.83198,26.93346 -15.1878,30.64161 -2.62156,2.89679 -7.70019,2.39802 -22.59508,-2.21904 -8.11727,-2.51617 -16.06028,-4.30586 -20.32463,-4.57947 -11.21682,-1.7957 -18.88746,8.09995 -28,14.65341 -27.91809,20.07783 -36.8097,24.33407 -43.5,20.82256 z"
style="fill:#000000" />
<path
sodipodi:nodetypes="cscsssssssccc"
inkscape:connector-curvature="0"
id="path2987"
d="m 338,310.79118 c -1.925,-0.46211 -8.55748,-0.57196 -14.73884,-0.24413 l -11.23883,0.59606 -1.16709,-2.56149 c -1.04742,-2.29882 -0.4417,-4.02378 5.9069,-16.82155 C 320.65283,283.91703 324.74157,274.35 325.84822,270.5 327.62523,264.31785 333,241.17153 333,239.70102 c 0,-0.30699 0.70714,-1.26531 1.57143,-2.12959 2.10347,-2.10348 9.18161,-2.03578 17.79224,0.17017 5.65285,1.4482 7.36756,2.39455 9.72002,5.36446 6.48214,8.18349 21.03837,15.40973 14.91994,28.32586 -1.41866,14.95087 -7.97079,34.16095 -14.83219,45.02554 C 359.76469,319.83743 342.69403,311.918 338,310.79118 z"
style="fill:#000000" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@ -0,0 +1,47 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
Item {
ControlledShape {
id: shape
anchors.fill: parent
strokeWidth: 4
strokeColor: "black"
fillColor: "transparent"
startX: 50
startY: 50
delegate: [
PathQuad {
x: 150
y: 50
controlX: cp.x
controlY: cp.y
}
]
}
Rectangle {
id: cp
color: "red"
width: 10
height: 10
SequentialAnimation on x {
loops: Animation.Infinite
NumberAnimation {
from: 0
to: shape.width - cp.width
duration: 5000
}
NumberAnimation {
from: shape.width - cp.width
to: 0
duration: 5000
}
}
}
}

View File

@ -0,0 +1,106 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
ControlledShape {
// fillGradient: RadialGradient {
// centerX: 100
// centerY: 100
// centerRadius: 100
// SequentialAnimation on focalRadius {
// loops: Animation.Infinite
// NumberAnimation {
// from: 1
// to: 20
// duration: 2000
// }
// NumberAnimation {
// from: 20
// to: 1
// duration: 2000
// }
// }
// SequentialAnimation on focalX {
// loops: Animation.Infinite
// NumberAnimation {
// from: 50
// to: 150
// duration: 3000
// }
// NumberAnimation {
// from: 150
// to: 50
// duration: 3000
// }
// }
// SequentialAnimation on focalY {
// loops: Animation.Infinite
// NumberAnimation {
// from: 50
// to: 150
// duration: 1000
// }
// NumberAnimation {
// from: 150
// to: 50
// duration: 1000
// }
// }
// GradientStop {
// position: 0
// color: "#ffffff"
// }
// GradientStop {
// position: 0.11
// color: "#f9ffa0"
// }
// GradientStop {
// position: 0.13
// color: "#f9ff99"
// }
// GradientStop {
// position: 0.14
// color: "#f3ff86"
// }
// GradientStop {
// position: 0.49
// color: "#93b353"
// }
// GradientStop {
// position: 0.87
// color: "#264619"
// }
// GradientStop {
// position: 0.96
// color: "#0c1306"
// }
// GradientStop {
// position: 1
// color: "#000000"
// }
// }
strokeWidth: 4
strokeColor: "red"
fillColor: "blue" // ignored with the gradient set
strokeStyle: ShapePath.DashLine
//dashPattern: [ 1, 4 ]
startX: 20
startY: 20
delegate: [
PathLine {
x: 180
y: 130
},
PathLine {
x: 20
y: 130
},
PathLine {
x: 20
y: 20
}
]
}

View File

@ -0,0 +1,115 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
Item {
ControlledShape {
id: circ1
anchors.fill: parent
fillColor: "transparent" // stroke only
strokeWidth: 4
SequentialAnimation on strokeColor {
loops: Animation.Infinite
ColorAnimation {
from: "black"
to: "yellow"
duration: 5000
}
ColorAnimation {
from: "yellow"
to: "green"
duration: 5000
}
ColorAnimation {
from: "green"
to: "black"
duration: 5000
}
}
readonly property real r: 60
startX: circ1.width / 2 - r
startY: circ1.height / 2 - r
delegate: [
PathArc {
x: circ1.width / 2 + circ1.r
y: circ1.height / 2 + circ1.r
radiusX: circ1.r
radiusY: circ1.r
useLargeArc: true
},
PathArc {
x: circ1.width / 2 - circ1.r
y: circ1.height / 2 - circ1.r
radiusX: circ1.r
radiusY: circ1.r
useLargeArc: true
}
]
}
ControlledShape {
id: circ2
anchors.fill: parent
SequentialAnimation on opacity {
loops: Animation.Infinite
NumberAnimation {
from: 1.0
to: 0.0
duration: 5000
}
NumberAnimation {
from: 0.0
to: 1.0
duration: 5000
}
}
strokeWidth: -1 // or strokeColor: "transparent"
SequentialAnimation on fillColor {
loops: Animation.Infinite
ColorAnimation {
from: "gray"
to: "purple"
duration: 3000
}
ColorAnimation {
from: "purple"
to: "red"
duration: 3000
}
ColorAnimation {
from: "red"
to: "gray"
duration: 3000
}
}
readonly property real r: 40
startX: circ2.width / 2 - r
startY: circ2.height / 2 - r
delegate: [
PathArc {
x: circ2.width / 2 + circ2.r
y: circ2.height / 2 + circ2.r
radiusX: circ2.r
radiusY: circ2.r
useLargeArc: true
},
PathArc {
x: circ2.width / 2 - circ2.r
y: circ2.height / 2 - circ2.r
radiusX: circ2.r
radiusY: circ2.r
useLargeArc: true
}
]
}
}

View File

@ -0,0 +1,62 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "svgpathloader.h"
#include <QFile>
#include <QPainterPath>
#include <QtSvg/private/qsvgtinydocument_p.h>
#include <QtSvg/private/qsvggraphics_p.h>
SvgPathLoader::SvgPathLoader()
{
connect(this, &SvgPathLoader::sourceChanged, this, &SvgPathLoader::loadPaths);
loadPaths();
}
void SvgPathLoader::loadPaths()
{
m_paths.clear();
m_fillColors.clear();
if (m_source.isEmpty())
return;
QString fileName;
if (m_source.isLocalFile())
fileName = m_source.toLocalFile();
else if (m_source.scheme() == QStringLiteral("qrc"))
fileName = QStringLiteral(":/") + m_source.fileName();
QFile f(fileName);
if (f.open(QIODevice::ReadOnly)) {
QXmlStreamReader reader(&f);
QString fillColor = QStringLiteral("#ffffff");
while (!reader.atEnd()) {
reader.readNext();
QXmlStreamAttributes attrs = reader.attributes();
if (reader.isStartElement() && attrs.hasAttribute(QStringLiteral("fill")))
fillColor = attrs.value(QStringLiteral("fill")).toString();
if (reader.isStartElement() && reader.name() == QStringLiteral("path")) {
m_fillColors.append(fillColor);
if (attrs.hasAttribute(QStringLiteral("d")))
m_paths.append(attrs.value(QStringLiteral("d")).toString());
if (attrs.hasAttribute(QStringLiteral("fill"))) {
m_fillColors[m_fillColors.size() - 1] = attrs.value(QStringLiteral("fill")).toString();
} else if (attrs.hasAttribute(QStringLiteral("style"))) {
QString s = attrs.value(QStringLiteral("style")).toString();
int idx = s.indexOf(QStringLiteral("fill:"));
if (idx >= 0) {
idx = s.indexOf(QLatin1Char('#'), idx);
if (idx >= 0)
m_fillColors[m_fillColors.size() - 1] = s.mid(idx, 7);
}
}
}
}
} else {
qWarning() << "Can't open file" << fileName;
}
emit pathsChanged();
}

View File

@ -0,0 +1,57 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef SVGPATHLOADER_H
#define SVGPATHLOADER_H
#include <QObject>
#include <QUrl>
#include <QPainterPath>
#include <QDebug>
class SvgPathLoader : public QObject
{
Q_OBJECT
Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
Q_PROPERTY(QStringList paths READ paths NOTIFY pathsChanged)
Q_PROPERTY(QStringList fillColors READ fillColors NOTIFY pathsChanged)
public:
SvgPathLoader();
QUrl source() const
{
return m_source;
}
void setSource(const QUrl &url)
{
if (url == m_source)
return;
m_source = url;
qDebug() << "Set source" << url;
emit sourceChanged();
}
QStringList paths() const
{
return m_paths;
}
QStringList fillColors() const
{
return m_fillColors;
}
private slots:
void loadPaths();
signals:
void sourceChanged();
void pathsChanged();
private:
QUrl m_source;
QStringList m_paths;
QStringList m_fillColors;
};
#endif // SVGPATHLOADER_H

View File

@ -0,0 +1,21 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
ControlledShape {
strokeColor: "black"
strokeWidth: 1
fillColor: "black"
delegate: [
PathText {
x: 0
y: 0
text: qsTr("Qt!")
font.family: "Arial"
font.pixelSize: 150
}
]
}

File diff suppressed because it is too large Load Diff