From 76c8e41fcc2fbe92e23e97a8e4a17b0dd7491e1c Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Fri, 23 Dec 2022 14:57:46 +0100 Subject: [PATCH] radiusX/radiusY to allow ellipsoid and circular radial gradients --- playground/gradients/GradientQuickShape.cpp | 2 +- playground/gradients/main.cpp | 2 +- playground/shapes/Window.cpp | 2 +- playground/shapes/shapes.qml | 8 +-- qmlexport/QskQml.cpp | 3 +- src/common/QskGradient.cpp | 67 ++++++++++----------- src/common/QskGradient.h | 7 +-- src/common/QskGradientDirection.cpp | 18 +++++- src/common/QskGradientDirection.h | 40 +++++++++--- src/nodes/QskGradientMaterial.cpp | 29 +++------ 10 files changed, 103 insertions(+), 75 deletions(-) diff --git a/playground/gradients/GradientQuickShape.cpp b/playground/gradients/GradientQuickShape.cpp index a26fa7de..82ffacc9 100644 --- a/playground/gradients/GradientQuickShape.cpp +++ b/playground/gradients/GradientQuickShape.cpp @@ -86,7 +86,7 @@ namespace g->setFocalX( dir.x() ); g->setFocalY( dir.y() ); - g->setCenterRadius( dir.radius() ); + g->setCenterRadius( qMax( dir.radiusX(), dir.radiusY() ) ); shapeGradient = g; break; diff --git a/playground/gradients/main.cpp b/playground/gradients/main.cpp index 978c7a9a..e9932eb9 100644 --- a/playground/gradients/main.cpp +++ b/playground/gradients/main.cpp @@ -53,7 +53,7 @@ namespace gradient.setSpreadMode( QskGradient::PadSpread ); #endif #if 0 - gradient.setRadialDirection( 0.25, 0.75, 0.25 ); + gradient.setRadialDirection( 0.25, 0.75, 0.25, 0.0 ); gradient.setSpreadMode( QskGradient::ReflectSpread ); #endif #if 0 diff --git a/playground/shapes/Window.cpp b/playground/shapes/Window.cpp index 3fa17b6d..0fe711a0 100644 --- a/playground/shapes/Window.cpp +++ b/playground/shapes/Window.cpp @@ -82,7 +82,7 @@ namespace shapeItem->setBorder( QskRgb::Indigo ); QskGradient gradient( QskRgb::LightYellow, QskRgb::MidnightBlue ); - gradient.setRadialDirection( QskRadialDirection() ); + gradient.setRadialDirection( QskRadialDirection( 0.7, 0.3, 0.25, 0.0 ) ); gradient.setSpreadMode( QskGradient::PadSpread ); shapeItem->setGradient( gradient ); diff --git a/playground/shapes/shapes.qml b/playground/shapes/shapes.qml index c609a157..3f62e5e8 100644 --- a/playground/shapes/shapes.qml +++ b/playground/shapes/shapes.qml @@ -171,7 +171,7 @@ Qsk.Window gradient: ({ - radial: { x: 0.5, y: 0.5, radius: 0.5 }, + radial: { x: 0.7, y: 0.3, radiusX: 0.25, radiusY: 0.0 }, stops: [ { position: 0.0, color: "LightYellow" }, @@ -187,7 +187,7 @@ Qsk.Window gradient: ({ - radial: { x: 0.5, y: 0.5, radius: 0.5 }, + radial: { x: 0.5, y: 0.5, radiusX: 0.5, radiusY: 0.5 }, spreadMode: Qsk.Gradient.PadSpread, stops: [ @@ -212,7 +212,7 @@ Qsk.Window gradient: ({ - radial: { x: 0.5, y: 0.7, radius: 0.25 }, + radial: { x: 0.5, y: 0.7, radiusX: 0.25, radiusY: 0.25 }, spreadMode: Qsk.Gradient.RepeatSpread, // LilyMeadow @@ -231,7 +231,7 @@ Qsk.Window gradient: ({ - radial: { x: 0.6, y: 0.4, radius: 0.1 }, + radial: { x: 0.6, y: 0.4, radiusX: 0.1, radiusY: 0.1 }, spreadMode: Qsk.Gradient.ReflectSpread, stops: [ diff --git a/qmlexport/QskQml.cpp b/qmlexport/QskQml.cpp index e2bfc803..a4bf4c0d 100644 --- a/qmlexport/QskQml.cpp +++ b/qmlexport/QskQml.cpp @@ -105,7 +105,8 @@ namespace return QskRadialDirection( value.property( QStringLiteral( "x" ) ).toNumber(), value.property( QStringLiteral( "y" ) ).toNumber(), - value.property( QStringLiteral( "radius" ) ).toNumber() ); + value.property( QStringLiteral( "radiusX" ) ).toNumber(), + value.property( QStringLiteral( "radiusY" ) ).toNumber() ); } QskGradient toGradient( const QJSValue& value ) diff --git a/src/common/QskGradient.cpp b/src/common/QskGradient.cpp index e1405771..f2b49165 100644 --- a/src/common/QskGradient.cpp +++ b/src/common/QskGradient.cpp @@ -56,24 +56,14 @@ static inline bool qskCanBeInterpolated( const QskGradient& from, const QskGradi return from.type() == to.type(); } -static inline QTransform qskTransformForRect( int stretch, const QRectF& rect ) +static inline QTransform qskTransformForRect( int, const QRectF& rect ) { const qreal x = rect.x(); const qreal y = rect.y(); const qreal w = rect.width(); const qreal h = rect.height(); - switch( stretch ) - { - case QskGradient::StretchToHeight: - return QTransform( h, 0, 0, h, x, y ); - - case QskGradient::StretchToWidth: - return QTransform( w, 0, 0, w, x, y ); - - default: - return QTransform( w, 0, 0, h, x, y ); - } + return QTransform( w, 0, 0, h, x, y ); } QskGradient::QskGradient( const QColor& color ) @@ -129,8 +119,7 @@ QskGradient::QskGradient( const QGradient& qGradient ) m_values[0] = g->focalPoint().x(); m_values[1] = g->focalPoint().y(); - m_values[2] = g->focalRadius(); - m_values[3] = 0.0; + m_values[3] = m_values[2] = g->focalRadius(); break; } @@ -258,7 +247,7 @@ void QskGradient::updateStatusBits() const case Radial: { - m_isVisible = m_values[2] > 0.0; // radius + m_isVisible = m_values[2] > 0.0 || m_values[3] > 0.0; // radius break; } @@ -425,9 +414,25 @@ void QskGradient::stretchTo( const QRectF& rect ) { transform.map( m_values[0], m_values[1], &m_values[0], &m_values[1] ); -#if 1 - m_values[2] *= qMin( rect.width(), rect.height() ); -#endif + qreal rx = qMax( m_values[2], 0.0 ); + qreal ry = qMax( m_values[3], 0.0 ); + + if ( rx == 0.0 || ry == 0.0 ) + { + /* + It would be more logical if the scaling happens according + the width, when rx is set ad v.v. But fitting the circle is + probably, what most use cases need - and how to specify + this. Maybe by introducing another stretchMode ... TODO + */ + const qreal r = qMin( rect.width(), rect.height() ) * qMax( rx, ry ); + m_values[2] = m_values[3] = r; + } + else + { + m_values[2] = rx * rect.width(); + m_values[3] = ry * rect.height(); + } break; } @@ -617,14 +622,18 @@ void QskGradient::setRadialDirection( const qreal x, qreal y, qreal radius ) setRadialDirection( QskRadialDirection( x, y, radius ) ); } +void QskGradient::setRadialDirection( const qreal x, qreal y,qreal radiusX, qreal radiusY ) +{ + setRadialDirection( QskRadialDirection( x, y, radiusX, radiusY ) ); +} void QskGradient::setRadialDirection( const QskRadialDirection& direction ) { m_type = Radial; m_values[0] = direction.center().x(); m_values[1] = direction.center().y(); - m_values[2] = direction.radius(); - m_values[3] = 0.0; + m_values[2] = direction.radiusX(); + m_values[3] = direction.radiusY(); } QskRadialDirection QskGradient::radialDirection() const @@ -634,7 +643,7 @@ QskRadialDirection QskGradient::radialDirection() const if ( m_type != Radial ) return QskRadialDirection( 0.5, 0.5, 0.0 ); - return QskRadialDirection( m_values[0], m_values[1], m_values[2] ); + return QskRadialDirection( m_values[0], m_values[1], m_values[2], m_values[3] ); } void QskGradient::setConicDirection( qreal x, qreal y ) @@ -785,7 +794,7 @@ QDebug operator<<( QDebug debug, const QskGradient& gradient ) const auto dir = gradient.radialDirection(); debug << dir.center().x() << "," << dir.center().y() - << "," << dir.radius() << ")"; + << "," << dir.radiusX() << dir.radiusY() << ")"; break; } @@ -831,19 +840,9 @@ QDebug operator<<( QDebug debug, const QskGradient& gradient ) } } - switch( static_cast< int >( gradient.stretchMode() ) ) + if ( gradient.stretchMode() == QskGradient::StretchToSize ) { - case QskGradient::StretchToSize: - debug << " SS"; - break; - - case QskGradient::StretchToHeight: - debug << " SH"; - break; - - case QskGradient::StretchToWidth: - debug << " SW"; - break; + debug << " SS"; } switch( gradient.spreadMode() ) diff --git a/src/common/QskGradient.h b/src/common/QskGradient.h index 5e6a33bb..8c674212 100644 --- a/src/common/QskGradient.h +++ b/src/common/QskGradient.h @@ -61,10 +61,6 @@ class QSK_EXPORT QskGradient enum StretchMode { NoStretch, - - StretchToHeight, - StretchToWidth, - StretchToSize }; Q_ENUM( StretchMode ) @@ -98,6 +94,7 @@ class QSK_EXPORT QskGradient void setRadialDirection( const QskRadialDirection& ); void setRadialDirection( const qreal x, qreal y, qreal radius ); + void setRadialDirection( const qreal x, qreal y, qreal radiusX, qreal radiusY ); QskRadialDirection radialDirection() const; void setConicDirection( qreal, qreal ); @@ -174,7 +171,7 @@ class QSK_EXPORT QskGradient /* Linear: x1, y1, x2, y2 - Radial: centerX, centerY, radius, n/a + Radial: centerX, centerY, radiusX, radiusY Conic: centerX, centerY, startAngle, spanAngle */ qreal m_values[4] = {}; diff --git a/src/common/QskGradientDirection.cpp b/src/common/QskGradientDirection.cpp index e4543687..7ca00e6b 100644 --- a/src/common/QskGradientDirection.cpp +++ b/src/common/QskGradientDirection.cpp @@ -142,9 +142,25 @@ void QskRadialDirection::setY( qreal y ) noexcept m_y = y; } +void QskRadialDirection::setRadiusX( qreal radius ) noexcept +{ + m_radiusX = radius; +} + +void QskRadialDirection::setRadiusY( qreal radius ) noexcept +{ + m_radiusY = radius; +} + void QskRadialDirection::setRadius( qreal radius ) noexcept { - m_radius = radius; + m_radiusX = m_radiusY = radius; +} + +void QskRadialDirection::setRadius( qreal radiusX, qreal radiusY ) noexcept +{ + m_radiusX = radiusX; + m_radiusY = radiusY; } #include "moc_QskGradientDirection.cpp" diff --git a/src/common/QskGradientDirection.h b/src/common/QskGradientDirection.h index a7afeb25..36df36b2 100644 --- a/src/common/QskGradientDirection.h +++ b/src/common/QskGradientDirection.h @@ -113,16 +113,18 @@ class QSK_EXPORT QskRadialDirection Q_PROPERTY( qreal x READ x WRITE setX ) Q_PROPERTY( qreal y READ y WRITE setY ) - Q_PROPERTY( qreal radius READ radius WRITE setRadius ) + Q_PROPERTY( qreal radiusX READ radiusX WRITE setRadiusX ) + Q_PROPERTY( qreal radiusY READ radiusY WRITE setRadiusY ) public: constexpr QskRadialDirection() noexcept = default; constexpr QskRadialDirection( const QPointF& center, qreal radius ) noexcept; constexpr QskRadialDirection( qreal cx, qreal cy, qreal radius ) noexcept; + constexpr QskRadialDirection( qreal cx, qreal cy, qreal radiusX, qreal radiusY ) noexcept; constexpr QPointF center() const noexcept; - void setCenter(const QPointF& center) noexcept; + void setCenter(const QPointF& ) noexcept; void setCenter(qreal x, qreal y) noexcept; void setX( qreal ) noexcept; @@ -131,13 +133,20 @@ class QSK_EXPORT QskRadialDirection void setY( qreal ) noexcept; constexpr qreal y() const noexcept; - constexpr qreal radius() const noexcept; - void setRadius( qreal radius ) noexcept; + constexpr qreal radiusX() const noexcept; + void setRadiusX( qreal ) noexcept; + + constexpr qreal radiusY() const noexcept; + void setRadiusY( qreal ) noexcept; + + void setRadius( qreal ) noexcept; + void setRadius( qreal, qreal ) noexcept; private: qreal m_x = 0.5; qreal m_y = 0.5; - qreal m_radius = 0.5; + qreal m_radiusX = 0.5; + qreal m_radiusY = 0.5; }; inline constexpr QskLinearDirection::QskLinearDirection( @@ -270,7 +279,17 @@ inline constexpr QskRadialDirection::QskRadialDirection( qreal x, qreal y, qreal radius ) noexcept : m_x( x ) , m_y( y ) - , m_radius( radius ) + , m_radiusX( radius ) + , m_radiusY( radius ) +{ +} + +inline constexpr QskRadialDirection::QskRadialDirection( + qreal x, qreal y, qreal radiusX, qreal radiusY ) noexcept + : m_x( x ) + , m_y( y ) + , m_radiusX( radiusX ) + , m_radiusY( radiusY ) { } @@ -295,9 +314,14 @@ inline constexpr qreal QskRadialDirection::y() const noexcept return m_y; } -inline constexpr qreal QskRadialDirection::radius() const noexcept +inline constexpr qreal QskRadialDirection::radiusX() const noexcept { - return m_radius; + return m_radiusX; +} + +inline constexpr qreal QskRadialDirection::radiusY() const noexcept +{ + return m_radiusY; } #endif diff --git a/src/nodes/QskGradientMaterial.cpp b/src/nodes/QskGradientMaterial.cpp index 2e3cf819..3f23e777 100644 --- a/src/nodes/QskGradientMaterial.cpp +++ b/src/nodes/QskGradientMaterial.cpp @@ -70,7 +70,7 @@ namespace virtual QSGMaterialShader* createShader() const = 0; #endif - virtual bool setGradient( const QRectF&, const QskGradient& ) = 0; + virtual bool setGradient( const QskGradient& ) = 0; }; #ifdef SHADER_GL @@ -170,7 +170,7 @@ namespace { } - bool setGradient( const QRectF& rect, const QskGradient& gradient ) override + bool setGradient( const QskGradient& gradient ) override { bool changed = false; @@ -194,11 +194,8 @@ namespace const auto dir = gradient.linearDirection(); - const QVector4D vector( - rect.left() + dir.x1() * rect.width(), - rect.top() + dir.y1() * rect.height(), - ( dir.x2() - dir.x1() ) * rect.width(), - ( dir.y2() - dir.y1() ) * rect.height() ); + const QVector4D vector( dir.x1(), dir.y1(), + dir.x2() - dir.x1(), dir.y2() - dir.y1() ); if ( m_gradientVector != vector ) { @@ -333,7 +330,7 @@ namespace return &type; } - bool setGradient( const QRectF& rect, const QskGradient& gradient ) override + bool setGradient( const QskGradient& gradient ) override { bool changed = false; @@ -351,12 +348,8 @@ namespace const auto dir = gradient.radialDirection(); - const QVector2D pos( - rect.left() + dir.center().x() * rect.width(), - rect.top() + dir.center().y() * rect.height() ); - - const QVector2D radius( dir.radius() * rect.width(), - dir.radius() * rect.height() ); + const QVector2D pos( dir.x(), dir.y() ); + const QVector2D radius( dir.radiusX(), dir.radiusY() ); if ( ( pos != m_center ) || ( m_radius != radius ) ) { @@ -504,7 +497,7 @@ namespace return &type; } - bool setGradient( const QRectF& rect, const QskGradient& gradient ) override + bool setGradient( const QskGradient& gradient ) override { bool changed = false; @@ -522,9 +515,7 @@ namespace const auto dir = gradient.conicDirection(); - const QVector2D center( - rect.left() + dir.center().x() * rect.width(), - rect.top() + dir.center().y() * rect.height() ); + const QVector2D center( dir.x(), dir.y() ); if ( center != m_center ) { @@ -715,7 +706,7 @@ bool QskGradientMaterial::updateGradient( const QRectF& rect, const QskGradient& case QskGradient::Conic: { auto material = static_cast< GradientMaterial* >( this ); - return material->setGradient( rect, gradient ); + return material->setGradient( gradient.stretchedTo( rect ) ); } default: