diff options
author | Eirik Aavitsland <[email protected]> | 2024-04-23 13:52:21 +0200 |
---|---|---|
committer | Eirik Aavitsland <[email protected]> | 2024-05-14 20:06:17 +0200 |
commit | 31d652d73b20f43b492ba8c306f932ca6fc3a0c1 (patch) | |
tree | 3d80e82650cb273b0608c8216865aa8966563826 | |
parent | 8181d2874e2f3825a22fef667b2f5f5b3784dee9 (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]>
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 { |