aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEirik Aavitsland <[email protected]>2024-04-23 13:52:21 +0200
committerEirik Aavitsland <[email protected]>2024-05-14 20:06:17 +0200
commit31d652d73b20f43b492ba8c306f932ca6fc3a0c1 (patch)
tree3d80e82650cb273b0608c8216865aa8966563826
parent8181d2874e2f3825a22fef667b2f5f5b3784dee9 (diff)
Add fillTransform property to ShapePath
This adds functionality corresponding to QBrush transform to QuickShapes. Change-Id: I2b5903f8c228adec65a6f5be64e3816143879302 Reviewed-by: Eskil Abrahamsen Blomfeldt <[email protected]>
-rw-r--r--examples/quick/quickshapes/shapes/CMakeLists.txt1
-rw-r--r--examples/quick/quickshapes/shapes/fillTransform.qml51
-rw-r--r--examples/quick/quickshapes/shapes/shapegallery.qml4
-rw-r--r--examples/quick/quickshapes/shapes/shapes.qrc1
-rw-r--r--src/quick/CMakeLists.txt1
-rw-r--r--src/quick/scenegraph/qsgcurvefillnode_p.cpp16
-rw-r--r--src/quick/scenegraph/qsgcurvefillnode_p.h12
-rw-r--r--src/quick/scenegraph/shaders_ng/shapecurve.frag3
-rw-r--r--src/quick/scenegraph/shaders_ng/shapecurve.vert10
-rw-r--r--src/quick/scenegraph/util/qsgtransform.cpp10
-rw-r--r--src/quick/scenegraph/util/qsgtransform_p.h105
-rw-r--r--src/quickshapes/qquickshape.cpp28
-rw-r--r--src/quickshapes/qquickshape_p.h5
-rw-r--r--src/quickshapes/qquickshape_p_p.h6
-rw-r--r--src/quickshapes/qquickshapecurverenderer.cpp8
-rw-r--r--src/quickshapes/qquickshapecurverenderer_p.h2
-rw-r--r--src/quickshapes/qquickshapegenericrenderer.cpp72
-rw-r--r--src/quickshapes/qquickshapegenericrenderer_p.h9
-rw-r--r--src/quickshapes/qquickshapesoftwarerenderer.cpp9
-rw-r--r--src/quickshapes/qquickshapesoftwarerenderer_p.h1
-rw-r--r--src/quickshapes/shaders_ng/conicalgradient.frag1
-rw-r--r--src/quickshapes/shaders_ng/conicalgradient.vert4
-rw-r--r--src/quickshapes/shaders_ng/lineargradient.frag1
-rw-r--r--src/quickshapes/shaders_ng/lineargradient.vert4
-rw-r--r--src/quickshapes/shaders_ng/radialgradient.frag1
-rw-r--r--src/quickshapes/shaders_ng/radialgradient.vert4
-rw-r--r--tests/auto/quick/qquickshape/data/filltransform.qml58
-rw-r--r--tests/auto/quick/qquickshape/tst_qquickshape.cpp37
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_gradient_xf.qml81
-rw-r--r--tests/baseline/scenegraph/data/shape/shape_spread_xf.qml47
-rw-r--r--tests/manual/painterpathquickshape/ControlPanel.qml14
-rw-r--r--tests/manual/painterpathquickshape/ControlledShape.qml1
32 files changed, 581 insertions, 26 deletions
diff --git a/examples/quick/quickshapes/shapes/CMakeLists.txt b/examples/quick/quickshapes/shapes/CMakeLists.txt
index 5036f1a37e..4618309ca0 100644
--- a/examples/quick/quickshapes/shapes/CMakeLists.txt
+++ b/examples/quick/quickshapes/shapes/CMakeLists.txt
@@ -52,6 +52,7 @@ qt_add_qml_module(shapesexample
"tapableTriangle.qml"
"tiger.qml"
"zoomtiger.qml"
+ "fillTransform.qml"
)
install(TARGETS shapesexample
diff --git a/examples/quick/quickshapes/shapes/fillTransform.qml b/examples/quick/quickshapes/shapes/fillTransform.qml
new file mode 100644
index 0000000000..f2f39d2ea6
--- /dev/null
+++ b/examples/quick/quickshapes/shapes/fillTransform.qml
@@ -0,0 +1,51 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Shapes
+
+Rectangle {
+ color: "lightGray"
+ width: 256
+ height: 256
+
+ Shape {
+ anchors.centerIn: parent
+ width: 200
+ height: 100
+
+ ShapePath {
+ id: path1
+ fillGradient: RadialGradient {
+ id: grad
+ centerX: path1.startX + 100
+ centerY: path1.startY + 50
+ centerRadius: 50
+ focalX: centerX
+ focalY: centerY
+ GradientStop { position: 0.0; color: "blue" }
+ GradientStop { position: 0.5; color: "cyan" }
+ GradientStop { position: 1.0; color: "blue" }
+ }
+
+ property real fillScale: 1
+ NumberAnimation on fillScale {
+ running: true
+ loops: Animation.Infinite
+ from: 1
+ to: 5
+ duration: 3000
+ easing.type: Easing.InQuad
+ }
+
+ fillTransform: PlanarTransform.fromScale(fillScale, 1, grad.centerX, grad.centerY)
+
+ startX: 0
+ startY: 0
+ PathLine { relativeX: 200; relativeY: 0 }
+ PathLine { relativeX: 0; relativeY: 100 }
+ PathLine { relativeX: -200; relativeY: 0 }
+ PathLine { relativeX: 0; relativeY: -100 }
+ }
+ }
+}
diff --git a/examples/quick/quickshapes/shapes/shapegallery.qml b/examples/quick/quickshapes/shapes/shapegallery.qml
index 74059b8208..eb7c204c16 100644
--- a/examples/quick/quickshapes/shapes/shapegallery.qml
+++ b/examples/quick/quickshapes/shapes/shapegallery.qml
@@ -146,6 +146,10 @@ Rectangle {
name: qsTr("Text")
shapeUrl: "text.qml"
}
+ ListElement {
+ name: qsTr("Fill transform")
+ shapeUrl: "fillTransform.qml"
+ }
}
}
}
diff --git a/examples/quick/quickshapes/shapes/shapes.qrc b/examples/quick/quickshapes/shapes/shapes.qrc
index 413816dba2..ea25f9d43d 100644
--- a/examples/quick/quickshapes/shapes/shapes.qrc
+++ b/examples/quick/quickshapes/shapes/shapes.qrc
@@ -24,5 +24,6 @@
<file>tigerLoader.qml</file>
<file>text.qml</file>
<file>zoomtiger.qml</file>
+ <file>fillTransform.qml</file>
</qresource>
</RCC>
diff --git a/src/quick/CMakeLists.txt b/src/quick/CMakeLists.txt
index 86edb893c2..0948423662 100644
--- a/src/quick/CMakeLists.txt
+++ b/src/quick/CMakeLists.txt
@@ -181,6 +181,7 @@ qt_internal_add_qml_module(Quick
scenegraph/util/qsgvertexcolormaterial.cpp scenegraph/util/qsgvertexcolormaterial.h
scenegraph/util/qquadpath.cpp scenegraph/util/qquadpath_p.h
scenegraph/util/qsggradientcache.cpp scenegraph/util/qsggradientcache_p.h
+ scenegraph/util/qsgtransform.cpp scenegraph/util/qsgtransform_p.h
util/qminimalflatset_p.h
util/qquickanimation.cpp util/qquickanimation_p.h
util/qquickanimation_p_p.h
diff --git a/src/quick/scenegraph/qsgcurvefillnode_p.cpp b/src/quick/scenegraph/qsgcurvefillnode_p.cpp
index 387f4b3ce6..d8d2a9bdcc 100644
--- a/src/quick/scenegraph/qsgcurvefillnode_p.cpp
+++ b/src/quick/scenegraph/qsgcurvefillnode_p.cpp
@@ -125,7 +125,18 @@ namespace {
}
offset += 16;
- } else if (newNode->gradientType() == QGradient::LinearGradient) {
+ } else {
+ Q_ASSERT(buf->size() >= offset + 64);
+
+ if (!oldNode || *oldNode->fillTransform() != *newNode->fillTransform()) {
+ memcpy(buf->data() + offset, newNode->fillTransform()->invertedData(), 64);
+ changed = true;
+ }
+
+ offset += 64;
+ }
+
+ if (newNode->gradientType() == QGradient::LinearGradient) {
Q_ASSERT(buf->size() >= offset + 8 + 8);
QVector2D newGradientStart = QVector2D(newNode->fillGradient()->a);
@@ -285,6 +296,9 @@ int QSGCurveFillMaterial::compare(const QSGMaterial *other) const
if (int d = ga.stops[i].second.rgba() - gb.stops[i].second.rgba())
return d;
}
+
+ if (int d = a->fillTransform()->compareTo(*b->fillTransform()))
+ return d;
}
return 0;
diff --git a/src/quick/scenegraph/qsgcurvefillnode_p.h b/src/quick/scenegraph/qsgcurvefillnode_p.h
index c90c5b8d38..8a4180ccfe 100644
--- a/src/quick/scenegraph/qsgcurvefillnode_p.h
+++ b/src/quick/scenegraph/qsgcurvefillnode_p.h
@@ -8,6 +8,7 @@
#include <QtQuick/qtquickexports.h>
#include <QtQuick/private/qsggradientcache_p.h>
+#include <QtQuick/private/qsgtransform_p.h>
#include <QtQuick/qsgnode.h>
#include "qsgcurveabstractnode_p.h"
@@ -66,6 +67,16 @@ public:
return m_gradientType;
}
+ void setFillTransform(const QSGTransform &transform)
+ {
+ m_fillTransform = transform;
+ }
+
+ const QSGTransform *fillTransform() const
+ {
+ return &m_fillTransform;
+ }
+
float debug() const
{
return m_debug;
@@ -186,6 +197,7 @@ private:
float m_debug = 0.0f;
QSGGradientCache::GradientDesc m_fillGradient;
QGradient::Type m_gradientType = QGradient::NoGradient;
+ QSGTransform m_fillTransform;
QScopedPointer<QSGMaterial> m_material;
diff --git a/src/quick/scenegraph/shaders_ng/shapecurve.frag b/src/quick/scenegraph/shaders_ng/shapecurve.frag
index bd0d02b73b..f170ad780b 100644
--- a/src/quick/scenegraph/shaders_ng/shapecurve.frag
+++ b/src/quick/scenegraph/shaders_ng/shapecurve.frag
@@ -26,6 +26,9 @@ layout(std140, binding = 0) uniform buf {
float debug;
float reserved3;
+#if defined(LINEARGRADIENT) || defined(RADIALGRADIENT) || defined(CONICALGRADIENT)
+ mat4 gradientMatrix;
+#endif
#if defined(LINEARGRADIENT)
vec2 gradientStart;
vec2 gradientEnd;
diff --git a/src/quick/scenegraph/shaders_ng/shapecurve.vert b/src/quick/scenegraph/shaders_ng/shapecurve.vert
index 688aa83139..ce6fa41f7b 100644
--- a/src/quick/scenegraph/shaders_ng/shapecurve.vert
+++ b/src/quick/scenegraph/shaders_ng/shapecurve.vert
@@ -25,6 +25,9 @@ layout(std140, binding = 0) uniform buf {
float debug;
float reserved3;
+#if defined(LINEARGRADIENT) || defined(RADIALGRADIENT) || defined(CONICALGRADIENT)
+ mat4 gradientMatrix;
+#endif
#if defined(LINEARGRADIENT)
vec2 gradientStart;
vec2 gradientEnd;
@@ -64,11 +67,14 @@ void main()
gradient = vertexGradient / ubuf.matrixScale;
+#if defined(LINEARGRADIENT) || defined(RADIALGRADIENT) || defined(CONICALGRADIENT)
+ vec2 gradVertexCoord = (ubuf.gradientMatrix * vertexCoord).xy;
+#endif
#if defined(LINEARGRADIENT)
vec2 gradVec = ubuf.gradientEnd - ubuf.gradientStart;
- gradTabIndex = dot(gradVec, vertexCoord.xy - ubuf.gradientStart.xy) / dot(gradVec, gradVec);
+ gradTabIndex = dot(gradVec, gradVertexCoord - ubuf.gradientStart) / dot(gradVec, gradVec);
#elif defined(RADIALGRADIENT) || defined(CONICALGRADIENT)
- coord = vertexCoord.xy - ubuf.translationPoint;
+ coord = gradVertexCoord - ubuf.translationPoint;
#endif
#if QSHADER_VIEW_COUNT >= 2
diff --git a/src/quick/scenegraph/util/qsgtransform.cpp b/src/quick/scenegraph/util/qsgtransform.cpp
new file mode 100644
index 0000000000..7efb014c33
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgtransform.cpp
@@ -0,0 +1,10 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qsgtransform_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QMatrix4x4 QSGTransform::m_identity;
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgtransform_p.h b/src/quick/scenegraph/util/qsgtransform_p.h
new file mode 100644
index 0000000000..f19d7a0efd
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgtransform_p.h
@@ -0,0 +1,105 @@
+// Copyright (C) 2024 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 QSGTRANSFORM_P_H
+#define QSGTRANSFORM_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 <QSharedPointer>
+#include <QMatrix4x4>
+#include <QtQuick/qtquickexports.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_EXPORT QSGTransform
+{
+public:
+ void setMatrix(const QMatrix4x4 &matrix)
+ {
+ if (matrix.isIdentity())
+ m_matrixPtr.clear();
+ else
+ m_matrixPtr = QSharedPointer<QMatrix4x4>::create(matrix);
+ m_invertedPtr.clear();
+ }
+
+ QMatrix4x4 matrix() const
+ {
+ return m_matrixPtr ? *m_matrixPtr : m_identity;
+ }
+
+ bool isIdentity() const
+ {
+ return !m_matrixPtr;
+ }
+
+ bool operator==(const QMatrix4x4 &other) const
+ {
+ return m_matrixPtr ? (other == *m_matrixPtr) : other.isIdentity();
+ }
+
+ bool operator!=(const QMatrix4x4 &other) const
+ {
+ return !(*this == other);
+ }
+
+ bool operator==(const QSGTransform &other) const
+ {
+ return (m_matrixPtr == other.m_matrixPtr)
+ || (m_matrixPtr && other.m_matrixPtr && *m_matrixPtr == *other.m_matrixPtr);
+ }
+
+ bool operator!=(const QSGTransform &other) const
+ {
+ return !(*this == other);
+ }
+
+ int compareTo(const QSGTransform &other) const
+ {
+ int diff = 0;
+ if (m_matrixPtr != other.m_matrixPtr) {
+ if (m_matrixPtr.isNull()) {
+ diff = -1;
+ } else if (other.m_matrixPtr.isNull()) {
+ diff = 1;
+ } else {
+ const float *ptr1 = m_matrixPtr->constData();
+ const float *ptr2 = other.m_matrixPtr->constData();
+ for (int i = 0; i < 16 && !diff; i++) {
+ float d = ptr1[i] - ptr2[i];
+ if (d != 0)
+ diff = (d > 0) ? 1 : -1;
+ }
+ }
+ }
+ return diff;
+ }
+
+ const float *invertedData() const
+ {
+ if (!m_matrixPtr)
+ return m_identity.constData();
+ if (!m_invertedPtr)
+ m_invertedPtr = QSharedPointer<QMatrix4x4>::create(m_matrixPtr->inverted());
+ return m_invertedPtr->constData();
+ }
+
+private:
+ static QMatrix4x4 m_identity;
+ QSharedPointer<QMatrix4x4> m_matrixPtr;
+ mutable QSharedPointer<QMatrix4x4> m_invertedPtr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGTRANSFORM_P_H
diff --git a/src/quickshapes/qquickshape.cpp b/src/quickshapes/qquickshape.cpp
index 3bc3bc6c23..6505adcb39 100644
--- a/src/quickshapes/qquickshape.cpp
+++ b/src/quickshapes/qquickshape.cpp
@@ -570,6 +570,32 @@ void QQuickShapePath::setPathHints(PathHints newPathHints)
}
/*!
+ \qmlproperty matrix4x4 QtQuick.Shapes::ShapePath::fillTransform
+ \since 6.8
+
+ This property defines a transform to be applied to the path's fill pattern (gradient). It has
+ no effect if the fill is a solid color or transparent. By default no fill transform is enabled
+ and the value of this property is the \c identity matrix.
+*/
+
+QMatrix4x4 QQuickShapePath::fillTransform() const
+{
+ Q_D(const QQuickShapePath);
+ return d->sfp.fillTransform.matrix();
+}
+
+void QQuickShapePath::setFillTransform(const QMatrix4x4 &matrix)
+{
+ Q_D(QQuickShapePath);
+ if (d->sfp.fillTransform != matrix) {
+ d->sfp.fillTransform.setMatrix(matrix);
+ d->dirty |= QQuickShapePathPrivate::DirtyFillTransform;
+ emit fillTransformChanged();
+ emit shapePathChanged();
+ }
+}
+
+/*!
\qmltype Shape
//! \instantiates QQuickShape
\inqmlmodule QtQuick.Shapes
@@ -1368,6 +1394,8 @@ void QQuickShapePrivate::sync()
renderer->setStrokeStyle(i, p->strokeStyle(), p->dashOffset(), p->dashPattern());
if (dirty & QQuickShapePathPrivate::DirtyFillGradient)
renderer->setFillGradient(i, p->fillGradient());
+ if (dirty & QQuickShapePathPrivate::DirtyFillTransform)
+ renderer->setFillTransform(i, QQuickShapePathPrivate::get(p)->sfp.fillTransform);
dirty = 0;
}
diff --git a/src/quickshapes/qquickshape_p.h b/src/quickshapes/qquickshape_p.h
index effbce1f8e..30be74b775 100644
--- a/src/quickshapes/qquickshape_p.h
+++ b/src/quickshapes/qquickshape_p.h
@@ -197,6 +197,7 @@ class Q_QUICKSHAPES_EXPORT QQuickShapePath : public QQuickPath
Q_PROPERTY(QQuickShapeGradient *fillGradient READ fillGradient WRITE setFillGradient RESET resetFillGradient)
Q_PROPERTY(QSizeF scale READ scale WRITE setScale NOTIFY scaleChanged REVISION(1, 14))
Q_PROPERTY(PathHints pathHints READ pathHints WRITE setPathHints NOTIFY pathHintsChanged REVISION(6, 7) FINAL)
+ Q_PROPERTY(QMatrix4x4 fillTransform READ fillTransform WRITE setFillTransform NOTIFY fillTransformChanged REVISION(6, 8) FINAL)
QML_NAMED_ELEMENT(ShapePath)
QML_ADDED_IN_VERSION(1, 0)
@@ -279,6 +280,9 @@ public:
PathHints pathHints() const;
void setPathHints(PathHints newPathHints);
+ QMatrix4x4 fillTransform() const;
+ void setFillTransform(const QMatrix4x4 &matrix);
+
Q_SIGNALS:
void shapePathChanged();
void strokeColorChanged();
@@ -293,6 +297,7 @@ Q_SIGNALS:
void dashPatternChanged();
Q_REVISION(6, 7) void pathHintsChanged();
+ Q_REVISION(6, 8) void fillTransformChanged();
private:
Q_DISABLE_COPY(QQuickShapePath)
diff --git a/src/quickshapes/qquickshape_p_p.h b/src/quickshapes/qquickshape_p_p.h
index a36aa1e27f..ffa1068de9 100644
--- a/src/quickshapes/qquickshape_p_p.h
+++ b/src/quickshapes/qquickshape_p_p.h
@@ -18,6 +18,7 @@
#include <QtQuickShapes/private/qquickshapesglobal_p.h>
#include <QtQuickShapes/private/qquickshape_p.h>
#include <private/qquickitem_p.h>
+#include <private/qsgtransform_p.h>
#include <QPainterPath>
#include <QColor>
#include <QBrush>
@@ -56,6 +57,7 @@ public:
virtual void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle,
qreal dashOffset, const QVector<qreal> &dashPattern) = 0;
virtual void setFillGradient(int index, QQuickShapeGradient *gradient) = 0;
+ virtual void setFillTransform(int index, const QSGTransform &transform) = 0;
virtual void setTriangulationScale(qreal) { }
// Render thread, with gui blocked
@@ -79,6 +81,7 @@ struct QQuickShapeStrokeFillParams
qreal dashOffset;
QVector<qreal> dashPattern;
QQuickShapeGradient *fillGradient;
+ QSGTransform fillTransform;
};
class Q_QUICKSHAPES_EXPORT QQuickShapePathPrivate : public QQuickPathPrivate
@@ -95,8 +98,9 @@ public:
DirtyStyle = 0x20,
DirtyDash = 0x40,
DirtyFillGradient = 0x80,
+ DirtyFillTransform = 0x100,
- DirtyAll = 0xFF
+ DirtyAll = 0x1FF
};
QQuickShapePathPrivate();
diff --git a/src/quickshapes/qquickshapecurverenderer.cpp b/src/quickshapes/qquickshapecurverenderer.cpp
index 856d83fdac..a163fd7781 100644
--- a/src/quickshapes/qquickshapecurverenderer.cpp
+++ b/src/quickshapes/qquickshapecurverenderer.cpp
@@ -267,6 +267,13 @@ void QQuickShapeCurveRenderer::setFillGradient(int index, QQuickShapeGradient *g
pd.m_dirty |= FillDirty;
}
+void QQuickShapeCurveRenderer::setFillTransform(int index, const QSGTransform &transform)
+{
+ auto &pathData = m_paths[index];
+ pathData.fillTransform = transform;
+ pathData.m_dirty |= FillDirty;
+}
+
void QQuickShapeCurveRenderer::setAsyncCallback(void (*callback)(void *), void *data)
{
m_asyncCallback = callback;
@@ -507,6 +514,7 @@ QQuickShapeCurveRenderer::NodeList QQuickShapeCurveRenderer::addFillNodes(const
QVector<quint32> indices = node->uncookedIndexes();
if (indices.size() > 0) {
node->setColor(color);
+ node->setFillTransform(pathData.fillTransform);
node->setFillGradient(pathData.gradient);
node->cookGeometry();
diff --git a/src/quickshapes/qquickshapecurverenderer_p.h b/src/quickshapes/qquickshapecurverenderer_p.h
index f2b10fb44c..a6f80824fd 100644
--- a/src/quickshapes/qquickshapecurverenderer_p.h
+++ b/src/quickshapes/qquickshapecurverenderer_p.h
@@ -55,6 +55,7 @@ public:
void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle,
qreal dashOffset, const QVector<qreal> &dashPattern) override;
void setFillGradient(int index, QQuickShapeGradient *gradient) override;
+ void setFillTransform(int index, const QSGTransform &transform) override;
void endSync(bool async) override;
void setAsyncCallback(void (*)(void *), void *) override;
Flags flags() const override { return SupportsAsync; }
@@ -94,6 +95,7 @@ private:
QGradient::Type gradientType = QGradient::NoGradient;
QSGGradientCache::GradientDesc gradient;
+ QSGTransform fillTransform;
QColor fillColor;
Qt::FillRule fillRule = Qt::OddEvenFill;
QPen pen;
diff --git a/src/quickshapes/qquickshapegenericrenderer.cpp b/src/quickshapes/qquickshapegenericrenderer.cpp
index a18479f776..20dfe21040 100644
--- a/src/quickshapes/qquickshapegenericrenderer.cpp
+++ b/src/quickshapes/qquickshapegenericrenderer.cpp
@@ -202,6 +202,13 @@ void QQuickShapeGenericRenderer::setFillGradient(int index, QQuickShapeGradient
d.syncDirty |= DirtyFillGradient;
}
+void QQuickShapeGenericRenderer::setFillTransform(int index, const QSGTransform &transform)
+{
+ ShapePathData &d(m_sp[index]);
+ d.fillTransform = transform;
+ d.syncDirty |= DirtyFillTransform;
+}
+
void QQuickShapeGenericRenderer::setTriangulationScale(qreal scale)
{
// No dirty, this is called at the start of every sync. Just store the value.
@@ -491,7 +498,7 @@ void QQuickShapeGenericRenderer::updateNode()
QQuickShapeGenericNode *node = *nodePtr;
if (m_accDirty & DirtyList)
- d.effectiveDirty |= DirtyFillGeom | DirtyStrokeGeom | DirtyColor | DirtyFillGradient;
+ d.effectiveDirty |= DirtyFillGeom | DirtyStrokeGeom | DirtyColor | DirtyFillGradient | DirtyFillTransform;
if (!d.effectiveDirty) {
prevNode = node;
@@ -544,6 +551,8 @@ void QQuickShapeGenericRenderer::updateShadowDataInNode(ShapePathData *d, QQuick
if (d->fillGradientActive) {
if (d->effectiveDirty & DirtyFillGradient)
n->m_fillGradient = d->fillGradient;
+ if (d->effectiveDirty & DirtyFillTransform)
+ n->m_fillTransform = d->fillTransform;
}
}
@@ -551,7 +560,7 @@ void QQuickShapeGenericRenderer::updateFillNode(ShapePathData *d, QQuickShapeGen
{
if (!node->m_fillNode)
return;
- if (!(d->effectiveDirty & (DirtyFillGeom | DirtyColor | DirtyFillGradient)))
+ if (!(d->effectiveDirty & (DirtyFillGeom | DirtyColor | DirtyFillGradient | DirtyFillTransform)))
return;
// Make a copy of the data that will be accessed by the material on
@@ -584,10 +593,10 @@ void QQuickShapeGenericRenderer::updateFillNode(ShapePathData *d, QQuickShapeGen
Q_UNREACHABLE_RETURN();
}
n->activateMaterial(m_item->window(), gradMat);
- if (d->effectiveDirty & DirtyFillGradient) {
+ if (d->effectiveDirty & (DirtyFillGradient | DirtyFillTransform)) {
// Gradients are implemented via a texture-based material.
n->markDirty(QSGNode::DirtyMaterial);
- // stop here if only the gradient changed; no need to touch the geometry
+ // stop here if only the gradient or filltransform changed; no need to touch the geometry
if (!(d->effectiveDirty & DirtyFillGeom))
return;
}
@@ -716,7 +725,7 @@ bool QQuickShapeLinearGradientRhiShader::updateUniformData(RenderState &state,
QQuickShapeLinearGradientMaterial *m = static_cast<QQuickShapeLinearGradientMaterial *>(newMaterial);
bool changed = false;
QByteArray *buf = state.uniformData();
- Q_ASSERT(buf->size() >= 84);
+ Q_ASSERT(buf->size() >= 84 + 64);
const int shaderMatrixCount = newMaterial->viewCount();
const int matrixCount = qMin(state.projectionMatrixCount(), shaderMatrixCount);
@@ -730,22 +739,28 @@ bool QQuickShapeLinearGradientRhiShader::updateUniformData(RenderState &state,
QQuickShapeGenericStrokeFillNode *node = m->node();
+ if (!oldMaterial || m_fillTransform != node->m_fillTransform) {
+ memcpy(buf->data() + 64 * shaderMatrixCount, node->m_fillTransform.invertedData(), 64);
+ m_fillTransform = node->m_fillTransform;
+ changed = true;
+ }
+
if (!oldMaterial || m_gradA.x() != node->m_fillGradient.a.x() || m_gradA.y() != node->m_fillGradient.a.y()) {
m_gradA = QVector2D(node->m_fillGradient.a.x(), node->m_fillGradient.a.y());
Q_ASSERT(sizeof(m_gradA) == 8);
- memcpy(buf->data() + 64 * shaderMatrixCount, &m_gradA, 8);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 64, &m_gradA, 8);
changed = true;
}
if (!oldMaterial || m_gradB.x() != node->m_fillGradient.b.x() || m_gradB.y() != node->m_fillGradient.b.y()) {
m_gradB = QVector2D(node->m_fillGradient.b.x(), node->m_fillGradient.b.y());
- memcpy(buf->data() + 64 * shaderMatrixCount + 8, &m_gradB, 8);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8, &m_gradB, 8);
changed = true;
}
if (state.isOpacityDirty()) {
const float opacity = state.opacity();
- memcpy(buf->data() + 64 * shaderMatrixCount + 8 + 8, &opacity, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8 + 8, &opacity, 4);
changed = true;
}
@@ -808,6 +823,9 @@ int QQuickShapeLinearGradientMaterial::compare(const QSGMaterial *other) const
return d;
}
+ if (int d = a->m_fillTransform.compareTo(b->m_fillTransform))
+ return d;
+
return 0;
}
@@ -830,7 +848,7 @@ bool QQuickShapeRadialGradientRhiShader::updateUniformData(RenderState &state,
QQuickShapeRadialGradientMaterial *m = static_cast<QQuickShapeRadialGradientMaterial *>(newMaterial);
bool changed = false;
QByteArray *buf = state.uniformData();
- Q_ASSERT(buf->size() >= 92);
+ Q_ASSERT(buf->size() >= 92 + 64);
const int shaderMatrixCount = newMaterial->viewCount();
const int matrixCount = qMin(state.projectionMatrixCount(), shaderMatrixCount);
@@ -844,6 +862,12 @@ bool QQuickShapeRadialGradientRhiShader::updateUniformData(RenderState &state,
QQuickShapeGenericStrokeFillNode *node = m->node();
+ if (!oldMaterial || m_fillTransform != node->m_fillTransform) {
+ memcpy(buf->data() + 64 * shaderMatrixCount, node->m_fillTransform.invertedData(), 64);
+ m_fillTransform = node->m_fillTransform;
+ changed = true;
+ }
+
const QPointF centerPoint = node->m_fillGradient.a;
const QPointF focalPoint = node->m_fillGradient.b;
const QPointF focalToCenter = centerPoint - focalPoint;
@@ -853,32 +877,32 @@ bool QQuickShapeRadialGradientRhiShader::updateUniformData(RenderState &state,
if (!oldMaterial || m_focalPoint.x() != focalPoint.x() || m_focalPoint.y() != focalPoint.y()) {
m_focalPoint = QVector2D(focalPoint.x(), focalPoint.y());
Q_ASSERT(sizeof(m_focalPoint) == 8);
- memcpy(buf->data() + 64 * shaderMatrixCount, &m_focalPoint, 8);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 64, &m_focalPoint, 8);
changed = true;
}
if (!oldMaterial || m_focalToCenter.x() != focalToCenter.x() || m_focalToCenter.y() != focalToCenter.y()) {
m_focalToCenter = QVector2D(focalToCenter.x(), focalToCenter.y());
Q_ASSERT(sizeof(m_focalToCenter) == 8);
- memcpy(buf->data() + 64 * shaderMatrixCount + 8, &m_focalToCenter, 8);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8, &m_focalToCenter, 8);
changed = true;
}
if (!oldMaterial || m_centerRadius != centerRadius) {
m_centerRadius = centerRadius;
- memcpy(buf->data() + 64 * shaderMatrixCount + 8 + 8, &m_centerRadius, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8 + 8, &m_centerRadius, 4);
changed = true;
}
if (!oldMaterial || m_focalRadius != focalRadius) {
m_focalRadius = focalRadius;
- memcpy(buf->data() + 64 * shaderMatrixCount + 8 + 8 + 4, &m_focalRadius, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8 + 8 + 4, &m_focalRadius, 4);
changed = true;
}
if (state.isOpacityDirty()) {
const float opacity = state.opacity();
- memcpy(buf->data() + 64 * shaderMatrixCount + 8 + 8 + 4 + 4, &opacity, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8 + 8 + 4 + 4, &opacity, 4);
changed = true;
}
@@ -946,6 +970,9 @@ int QQuickShapeRadialGradientMaterial::compare(const QSGMaterial *other) const
return d;
}
+ if (int d = a->m_fillTransform.compareTo(b->m_fillTransform))
+ return d;
+
return 0;
}
@@ -968,7 +995,7 @@ bool QQuickShapeConicalGradientRhiShader::updateUniformData(RenderState &state,
QQuickShapeConicalGradientMaterial *m = static_cast<QQuickShapeConicalGradientMaterial *>(newMaterial);
bool changed = false;
QByteArray *buf = state.uniformData();
- Q_ASSERT(buf->size() >= 80);
+ Q_ASSERT(buf->size() >= 80 + 64);
const int shaderMatrixCount = newMaterial->viewCount();
const int matrixCount = qMin(state.projectionMatrixCount(), shaderMatrixCount);
@@ -982,25 +1009,31 @@ bool QQuickShapeConicalGradientRhiShader::updateUniformData(RenderState &state,
QQuickShapeGenericStrokeFillNode *node = m->node();
+ if (!oldMaterial || m_fillTransform != node->m_fillTransform) {
+ memcpy(buf->data() + 64 * shaderMatrixCount, node->m_fillTransform.invertedData(), 64);
+ m_fillTransform = node->m_fillTransform;
+ changed = true;
+ }
+
const QPointF centerPoint = node->m_fillGradient.a;
const float angle = -qDegreesToRadians(node->m_fillGradient.v0);
if (!oldMaterial || m_centerPoint.x() != centerPoint.x() || m_centerPoint.y() != centerPoint.y()) {
m_centerPoint = QVector2D(centerPoint.x(), centerPoint.y());
Q_ASSERT(sizeof(m_centerPoint) == 8);
- memcpy(buf->data() + 64 * shaderMatrixCount, &m_centerPoint, 8);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 64, &m_centerPoint, 8);
changed = true;
}
if (!oldMaterial || m_angle != angle) {
m_angle = angle;
- memcpy(buf->data() + 64 * shaderMatrixCount + 8, &m_angle, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8, &m_angle, 4);
changed = true;
}
if (state.isOpacityDirty()) {
const float opacity = state.opacity();
- memcpy(buf->data() + 64 * shaderMatrixCount + 8 + 4, &opacity, 4);
+ memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8 + 4, &opacity, 4);
changed = true;
}
@@ -1059,6 +1092,9 @@ int QQuickShapeConicalGradientMaterial::compare(const QSGMaterial *other) const
return d;
}
+ if (int d = a->m_fillTransform.compareTo(b->m_fillTransform))
+ return d;
+
return 0;
}
diff --git a/src/quickshapes/qquickshapegenericrenderer_p.h b/src/quickshapes/qquickshapegenericrenderer_p.h
index 6877b674e0..f4a0f21487 100644
--- a/src/quickshapes/qquickshapegenericrenderer_p.h
+++ b/src/quickshapes/qquickshapegenericrenderer_p.h
@@ -40,7 +40,8 @@ public:
DirtyStrokeGeom = 0x02,
DirtyColor = 0x04,
DirtyFillGradient = 0x08,
- DirtyList = 0x10 // only for accDirty
+ DirtyFillTransform = 0x10,
+ DirtyList = 0x20 // only for accDirty
};
QQuickShapeGenericRenderer(QQuickItem *item)
@@ -64,6 +65,7 @@ public:
void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle,
qreal dashOffset, const QVector<qreal> &dashPattern) override;
void setFillGradient(int index, QQuickShapeGradient *gradient) override;
+ void setFillTransform(int index, const QSGTransform &transform) override;
void setTriangulationScale(qreal scale) override;
void endSync(bool async) override;
void setAsyncCallback(void (*)(void *), void *) override;
@@ -103,6 +105,7 @@ private:
QPainterPath path;
FillGradientType fillGradientActive;
QSGGradientCache::GradientDesc fillGradient;
+ QSGTransform fillTransform;
VertexContainerType fillVertices;
IndexContainerType fillIndices;
QSGGeometry::Type indexType;
@@ -190,6 +193,7 @@ public:
// shadow data for custom materials
QSGGradientCache::GradientDesc m_fillGradient;
+ QSGTransform m_fillTransform;
private:
QScopedPointer<QSGMaterial> m_material;
@@ -225,6 +229,7 @@ public:
QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
private:
+ QSGTransform m_fillTransform;
QVector2D m_gradA;
QVector2D m_gradB;
};
@@ -264,6 +269,7 @@ public:
QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
private:
+ QSGTransform m_fillTransform;
QVector2D m_focalPoint;
QVector2D m_focalToCenter;
float m_centerRadius;
@@ -300,6 +306,7 @@ public:
QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
private:
+ QSGTransform m_fillTransform;
QVector2D m_centerPoint;
float m_angle;
};
diff --git a/src/quickshapes/qquickshapesoftwarerenderer.cpp b/src/quickshapes/qquickshapesoftwarerenderer.cpp
index 60efcf1db9..12142a690e 100644
--- a/src/quickshapes/qquickshapesoftwarerenderer.cpp
+++ b/src/quickshapes/qquickshapesoftwarerenderer.cpp
@@ -138,6 +138,15 @@ void QQuickShapeSoftwareRenderer::setFillGradient(int index, QQuickShapeGradient
m_accDirty |= DirtyBrush;
}
+void QQuickShapeSoftwareRenderer::setFillTransform(int index, const QSGTransform &transform)
+{
+ ShapePathGuiData &d(m_sp[index]);
+ if (!(transform.isIdentity() && d.brush.transform().isIdentity())) // No need to copy if both==I
+ d.brush.setTransform(transform.matrix().toTransform());
+ d.dirty |= DirtyBrush;
+ m_accDirty |= DirtyBrush;
+}
+
void QQuickShapeSoftwareRenderer::endSync(bool)
{
}
diff --git a/src/quickshapes/qquickshapesoftwarerenderer_p.h b/src/quickshapes/qquickshapesoftwarerenderer_p.h
index d08145bb1b..12e4d0f038 100644
--- a/src/quickshapes/qquickshapesoftwarerenderer_p.h
+++ b/src/quickshapes/qquickshapesoftwarerenderer_p.h
@@ -47,6 +47,7 @@ public:
void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle,
qreal dashOffset, const QVector<qreal> &dashPattern) override;
void setFillGradient(int index, QQuickShapeGradient *gradient) override;
+ void setFillTransform(int index, const QSGTransform &transform) override;
void endSync(bool async) override;
void updateNode() override;
diff --git a/src/quickshapes/shaders_ng/conicalgradient.frag b/src/quickshapes/shaders_ng/conicalgradient.frag
index 7707d55deb..59862f991a 100644
--- a/src/quickshapes/shaders_ng/conicalgradient.frag
+++ b/src/quickshapes/shaders_ng/conicalgradient.frag
@@ -14,6 +14,7 @@ layout(std140, binding = 0) uniform buf {
#else
mat4 matrix;
#endif
+ mat4 gradientMatrix;
vec2 translationPoint;
float angle;
float opacity;
diff --git a/src/quickshapes/shaders_ng/conicalgradient.vert b/src/quickshapes/shaders_ng/conicalgradient.vert
index c092198724..cdaab16842 100644
--- a/src/quickshapes/shaders_ng/conicalgradient.vert
+++ b/src/quickshapes/shaders_ng/conicalgradient.vert
@@ -11,6 +11,7 @@ layout(std140, binding = 0) uniform buf {
#else
mat4 matrix;
#endif
+ mat4 gradientMatrix;
vec2 translationPoint;
float angle;
float opacity;
@@ -18,7 +19,8 @@ layout(std140, binding = 0) uniform buf {
void main()
{
- coord = vertexCoord.xy - ubuf.translationPoint;
+ vec2 gradVertexCoord = (ubuf.gradientMatrix * vertexCoord).xy;
+ coord = gradVertexCoord - ubuf.translationPoint;
#if QSHADER_VIEW_COUNT >= 2
gl_Position = ubuf.matrix[gl_ViewIndex] * vertexCoord;
#else
diff --git a/src/quickshapes/shaders_ng/lineargradient.frag b/src/quickshapes/shaders_ng/lineargradient.frag
index 6dc4c7d5ca..b6f0dc172a 100644
--- a/src/quickshapes/shaders_ng/lineargradient.frag
+++ b/src/quickshapes/shaders_ng/lineargradient.frag
@@ -14,6 +14,7 @@ layout(std140, binding = 0) uniform buf {
#else
mat4 matrix;
#endif
+ mat4 gradientMatrix;
vec2 gradStart;
vec2 gradEnd;
float opacity;
diff --git a/src/quickshapes/shaders_ng/lineargradient.vert b/src/quickshapes/shaders_ng/lineargradient.vert
index b453739230..13168b1c0f 100644
--- a/src/quickshapes/shaders_ng/lineargradient.vert
+++ b/src/quickshapes/shaders_ng/lineargradient.vert
@@ -11,6 +11,7 @@ layout(std140, binding = 0) uniform buf {
#else
mat4 matrix;
#endif
+ mat4 gradientMatrix;
vec2 gradStart;
vec2 gradEnd;
float opacity;
@@ -18,8 +19,9 @@ layout(std140, binding = 0) uniform buf {
void main()
{
+ vec2 gradVertexCoord = (ubuf.gradientMatrix * vertexCoord).xy;
vec2 gradVec = ubuf.gradEnd - ubuf.gradStart;
- gradTabIndex = dot(gradVec, vertexCoord.xy - ubuf.gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y);
+ gradTabIndex = dot(gradVec, gradVertexCoord - ubuf.gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y);
#if QSHADER_VIEW_COUNT >= 2
gl_Position = ubuf.matrix[gl_ViewIndex] * vertexCoord;
#else
diff --git a/src/quickshapes/shaders_ng/radialgradient.frag b/src/quickshapes/shaders_ng/radialgradient.frag
index bbc68b2ce7..cfbb44ac69 100644
--- a/src/quickshapes/shaders_ng/radialgradient.frag
+++ b/src/quickshapes/shaders_ng/radialgradient.frag
@@ -14,6 +14,7 @@ layout(std140, binding = 0) uniform buf {
#else
mat4 matrix;
#endif
+ mat4 gradientMatrix;
vec2 translationPoint;
vec2 focalToCenter;
float centerRadius;
diff --git a/src/quickshapes/shaders_ng/radialgradient.vert b/src/quickshapes/shaders_ng/radialgradient.vert
index 6d3dfce745..16c8406b23 100644
--- a/src/quickshapes/shaders_ng/radialgradient.vert
+++ b/src/quickshapes/shaders_ng/radialgradient.vert
@@ -11,6 +11,7 @@ layout(std140, binding = 0) uniform buf {
#else
mat4 matrix;
#endif
+ mat4 gradientMatrix;
vec2 translationPoint;
vec2 focalToCenter;
float centerRadius;
@@ -20,7 +21,8 @@ layout(std140, binding = 0) uniform buf {
void main()
{
- coord = vertexCoord.xy - ubuf.translationPoint;
+ vec2 gradVertexCoord = (ubuf.gradientMatrix * vertexCoord).xy;
+ coord = gradVertexCoord - ubuf.translationPoint;
#if QSHADER_VIEW_COUNT >= 2
gl_Position = ubuf.matrix[gl_ViewIndex] * vertexCoord;
#else
diff --git a/tests/auto/quick/qquickshape/data/filltransform.qml b/tests/auto/quick/qquickshape/data/filltransform.qml
new file mode 100644
index 0000000000..4f83c04c43
--- /dev/null
+++ b/tests/auto/quick/qquickshape/data/filltransform.qml
@@ -0,0 +1,58 @@
+import QtQuick
+import QtQuick.Shapes
+import tst_qquickpathitem
+
+Rectangle {
+ width: 440
+ height: 220
+ color: "white"
+
+ Shape {
+ objectName: "shape1"
+ ShapePath {
+ id: path1
+ objectName: "path1"
+ fillGradient: RadialGradient {
+ centerX: path1.startX + 100
+ centerY: path1.startY + 100
+ centerRadius: 100
+ focalX: centerX
+ focalY: centerY
+ GradientStop { position: 0.0; color: "blue" }
+ GradientStop { position: 0.5; color: "cyan" }
+ GradientStop { position: 1.0; color: "blue" }
+ }
+
+ fillTransform: PlanarTransform.fromScale(2, 1);
+
+ startX: 10
+ startY: 10
+ PathLine { relativeX: 200; relativeY: 0 }
+ PathLine { relativeX: 0; relativeY: 200 }
+ PathLine { relativeX: -200; relativeY: 0 }
+ PathLine { relativeX: 0; relativeY: -200 }
+ }
+
+ ShapePath {
+ id: path2
+ objectName: "path2"
+ fillGradient: RadialGradient {
+ centerX: path2.startX + 100
+ centerY: path2.startY + 100
+ centerRadius: 100
+ focalX: centerX
+ focalY: centerY
+ GradientStop { position: 0.0; color: "blue" }
+ GradientStop { position: 0.5; color: "cyan" }
+ GradientStop { position: 1.0; color: "blue" }
+ }
+
+ startX: 220 + 10
+ startY: 10
+ PathLine { relativeX: 200; relativeY: 0 }
+ PathLine { relativeX: 0; relativeY: 200 }
+ PathLine { relativeX: -200; relativeY: 0 }
+ PathLine { relativeX: 0; relativeY: -200 }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickshape/tst_qquickshape.cpp b/tests/auto/quick/qquickshape/tst_qquickshape.cpp
index f846cc4e4f..707e0037f5 100644
--- a/tests/auto/quick/qquickshape/tst_qquickshape.cpp
+++ b/tests/auto/quick/qquickshape/tst_qquickshape.cpp
@@ -62,6 +62,7 @@ private slots:
void multilineDataTypes_data();
void multilineDataTypes();
void multilineStronglyTyped();
+ void fillTransform();
private:
QVector<QPolygonF> m_lowPolyLogo;
@@ -247,6 +248,8 @@ void tst_QQuickShape::changeSignals()
QCOMPARE(vpChangeSpy.size(), 15);
qobject_cast<QQuickGradientStop *>(stopList.at(1))->setColor(Qt::black);
QCOMPARE(vpChangeSpy.size(), 16);
+ vp->setFillTransform(QMatrix4x4(QTransform::fromScale(3, 0.14)));
+ QCOMPARE(vpChangeSpy.size(), 17);
}
void tst_QQuickShape::render()
@@ -674,6 +677,40 @@ void tst_QQuickShape::multilineStronglyTyped()
}
}
+void tst_QQuickShape::fillTransform()
+{
+ QScopedPointer<QQuickView> window(createView());
+
+ window->setSource(testFileUrl("filltransform.qml"));
+ qApp->processEvents();
+
+ QQuickShape *obj = findItem<QQuickShape>(window->rootObject(), "shape1");
+ QVERIFY(obj != nullptr);
+ QQmlListReference list(obj, "data");
+ QCOMPARE(list.count(), 2);
+
+ QQuickShapePath *p1 = qobject_cast<QQuickShapePath *>(list.at(0));
+ QVERIFY(p1 != nullptr);
+ QVERIFY(p1->objectName() == "path1");
+ QVERIFY(p1->fillTransform() == QMatrix4x4(2,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1));
+
+ QQuickShapePath *p2 = qobject_cast<QQuickShapePath *>(list.at(1));
+ QVERIFY(p2 != nullptr);
+ QVERIFY(p2->objectName() == "path2");
+ QVERIFY(p2->fillTransform().isIdentity());
+
+ QMatrix4x4 xf(QTransform::fromTranslate(-36, 0).shear(0.35, 0));
+ p1->setFillTransform(xf);
+ QVERIFY(p1->fillTransform() == xf);
+
+ QVERIFY(p2->fillTransform().isIdentity());
+ p2->setFillTransform(xf);
+ QVERIFY(p2->fillTransform() == xf);
+
+ p1->setFillTransform(QMatrix4x4{});
+ QVERIFY(p1->fillTransform().isIdentity());
+}
+
QTEST_MAIN(tst_QQuickShape)
#include "tst_qquickshape.moc"
diff --git a/tests/baseline/scenegraph/data/shape/shape_gradient_xf.qml b/tests/baseline/scenegraph/data/shape/shape_gradient_xf.qml
new file mode 100644
index 0000000000..a0a33d9f2c
--- /dev/null
+++ b/tests/baseline/scenegraph/data/shape/shape_gradient_xf.qml
@@ -0,0 +1,81 @@
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ width: 320
+ height: 480
+
+ ListModel {
+ id: renderers
+ ListElement { renderer: Shape.GeometryRenderer }
+ ListElement { renderer: Shape.CurveRenderer }
+ }
+
+ Row {
+ Repeater {
+ model: renderers
+ Column {
+ Shape {
+ preferredRendererType: renderer
+ width: 160
+ height: 150
+
+ ShapePath {
+ strokeColor: "transparent"
+ fillGradient: LinearGradient {
+ y1: 50; y2: 80
+ GradientStop { position: 0; color: "black" }
+ GradientStop { position: 1; color: "cyan" }
+ }
+ fillTransform: PlanarTransform.fromAffineMatrix(0.8, 0.2, 0.3, 1.5, 20, -50 + startY)
+
+ startX: 10; startY: 10
+ PathLine { relativeX: 140; relativeY: 0 }
+ PathLine { relativeX: 0; relativeY: 100 }
+ PathLine { relativeX: -140; relativeY: 0 }
+ PathLine { relativeX: 0; relativeY: -100 }
+ }
+
+ ShapePath {
+ strokeColor: "transparent"
+ fillGradient: RadialGradient {
+ centerX: 80
+ centerY: 75
+ centerRadius: centerY
+ focalX: centerX
+ focalY: centerY
+ GradientStop { position: 0; color: "black" }
+ GradientStop { position: .5; color: "cyan" }
+ GradientStop { position: 1; color: "black" }
+ }
+ fillTransform: PlanarTransform.fromAffineMatrix(0.8, 0.2, 0.3, 1.5, 20, -50 + startY)
+
+ startX: 10; startY: 10 + 1 * 140
+ PathLine { relativeX: 140; relativeY: 0 }
+ PathLine { relativeX: 0; relativeY: 100 }
+ PathLine { relativeX: -140; relativeY: 0 }
+ PathLine { relativeX: 0; relativeY: -100 }
+ }
+
+ ShapePath {
+ strokeColor: "transparent"
+ fillGradient: ConicalGradient {
+ centerX: 80
+ centerY: 75
+ GradientStop { position: 0; color: "black" }
+ GradientStop { position: .5; color: "cyan" }
+ GradientStop { position: 1; color: "black" }
+ }
+ fillTransform: PlanarTransform.fromAffineMatrix(0.8, 0.2, 0.3, 1.5, 20, -50 + startY)
+
+ startX: 10; startY: 10 + 2 * 140
+ PathLine { relativeX: 140; relativeY: 0 }
+ PathLine { relativeX: 0; relativeY: 100 }
+ PathLine { relativeX: -140; relativeY: 0 }
+ PathLine { relativeX: 0; relativeY: -100 }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/baseline/scenegraph/data/shape/shape_spread_xf.qml b/tests/baseline/scenegraph/data/shape/shape_spread_xf.qml
new file mode 100644
index 0000000000..c9f67e472d
--- /dev/null
+++ b/tests/baseline/scenegraph/data/shape/shape_spread_xf.qml
@@ -0,0 +1,47 @@
+import QtQuick
+import QtQuick.Shapes
+
+Item {
+ width: 320
+ height: 480
+
+ ListModel {
+ id: renderers
+ ListElement { renderer: Shape.GeometryRenderer }
+ ListElement { renderer: Shape.CurveRenderer }
+ }
+
+ Row {
+ Repeater {
+ model: renderers
+ Column {
+ Repeater {
+ model: 3
+ Shape {
+ preferredRendererType: renderer
+ width: 160
+ height: 150
+ ShapePath {
+ strokeColor: "transparent"
+
+ fillGradient: LinearGradient {
+ id: grad
+ y1: 50; y2: 80
+ spread: model.index === 0 ? ShapeGradient.PadSpread : (model.index === 1 ? ShapeGradient.RepeatSpread : ShapeGradient.ReflectSpread)
+ GradientStop { position: 0; color: "black" }
+ GradientStop { position: 1; color: "red" }
+ }
+ fillTransform: PlanarTransform.fromShear(0, 0.2)
+
+ startX: 10; startY: 10
+ PathLine { relativeX: 140; relativeY: 0 }
+ PathLine { relativeX: 0; relativeY: 100 }
+ PathLine { relativeX: -140; relativeY: 0 }
+ PathLine { relativeX: 0; relativeY: -100 }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/manual/painterpathquickshape/ControlPanel.qml b/tests/manual/painterpathquickshape/ControlPanel.qml
index 87eb9ae3e7..ea3168d124 100644
--- a/tests/manual/painterpathquickshape/ControlPanel.qml
+++ b/tests/manual/painterpathquickshape/ControlPanel.qml
@@ -28,6 +28,7 @@ Item {
property alias painterComparisonAlpha: painterComparisonColorAlpha.value
property alias outlineEnabled: enableOutline.checked
property alias gradientType: gradientType.currentIndex
+ property alias fillScaleX: fillTransformSlider.value
property alias rendererName: rendererLabel.text
property alias preferCurve: rendererLabel.preferCurve
@@ -254,6 +255,19 @@ Item {
}
}
Label {
+ text: "Fill transform (scale x: " + fillTransformSlider.value.toFixed(2) + "):"
+ color: "white"
+ visible: gradientType.currentIndex != 0
+ }
+ Slider {
+ id: fillTransformSlider
+ Layout.fillWidth: true
+ from: 0.2
+ to: 5.0
+ value: 1.0
+ visible: gradientType.currentIndex != 0
+ }
+ Label {
text: "Fill alpha(" + Math.round(alphaSlider.value*100)/100 + "):"
color: "white"
}
diff --git a/tests/manual/painterpathquickshape/ControlledShape.qml b/tests/manual/painterpathquickshape/ControlledShape.qml
index e690f59ccc..26a57163cd 100644
--- a/tests/manual/painterpathquickshape/ControlledShape.qml
+++ b/tests/manual/painterpathquickshape/ControlledShape.qml
@@ -89,6 +89,7 @@ Item {
strokeStyle: controlPanel.outlineStyle
joinStyle: controlPanel.joinStyle
capStyle: controlPanel.capStyle
+ fillTransform: Qt.matrix4x4(controlPanel.fillScaleX,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1)
}
Repeater {