radiusX/radiusY to allow ellipsoid and circular radial gradients

This commit is contained in:
Uwe Rathmann 2022-12-23 14:57:46 +01:00
parent 877f4151bc
commit 76c8e41fcc
10 changed files with 103 additions and 75 deletions

View File

@ -86,7 +86,7 @@ namespace
g->setFocalX( dir.x() ); g->setFocalX( dir.x() );
g->setFocalY( dir.y() ); g->setFocalY( dir.y() );
g->setCenterRadius( dir.radius() ); g->setCenterRadius( qMax( dir.radiusX(), dir.radiusY() ) );
shapeGradient = g; shapeGradient = g;
break; break;

View File

@ -53,7 +53,7 @@ namespace
gradient.setSpreadMode( QskGradient::PadSpread ); gradient.setSpreadMode( QskGradient::PadSpread );
#endif #endif
#if 0 #if 0
gradient.setRadialDirection( 0.25, 0.75, 0.25 ); gradient.setRadialDirection( 0.25, 0.75, 0.25, 0.0 );
gradient.setSpreadMode( QskGradient::ReflectSpread ); gradient.setSpreadMode( QskGradient::ReflectSpread );
#endif #endif
#if 0 #if 0

View File

@ -82,7 +82,7 @@ namespace
shapeItem->setBorder( QskRgb::Indigo ); shapeItem->setBorder( QskRgb::Indigo );
QskGradient gradient( QskRgb::LightYellow, QskRgb::MidnightBlue ); QskGradient gradient( QskRgb::LightYellow, QskRgb::MidnightBlue );
gradient.setRadialDirection( QskRadialDirection() ); gradient.setRadialDirection( QskRadialDirection( 0.7, 0.3, 0.25, 0.0 ) );
gradient.setSpreadMode( QskGradient::PadSpread ); gradient.setSpreadMode( QskGradient::PadSpread );
shapeItem->setGradient( gradient ); shapeItem->setGradient( gradient );

View File

@ -171,7 +171,7 @@ Qsk.Window
gradient: 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: [ stops: [
{ position: 0.0, color: "LightYellow" }, { position: 0.0, color: "LightYellow" },
@ -187,7 +187,7 @@ Qsk.Window
gradient: 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, spreadMode: Qsk.Gradient.PadSpread,
stops: [ stops: [
@ -212,7 +212,7 @@ Qsk.Window
gradient: 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, spreadMode: Qsk.Gradient.RepeatSpread,
// LilyMeadow // LilyMeadow
@ -231,7 +231,7 @@ Qsk.Window
gradient: 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, spreadMode: Qsk.Gradient.ReflectSpread,
stops: [ stops: [

View File

@ -105,7 +105,8 @@ namespace
return QskRadialDirection( return QskRadialDirection(
value.property( QStringLiteral( "x" ) ).toNumber(), value.property( QStringLiteral( "x" ) ).toNumber(),
value.property( QStringLiteral( "y" ) ).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 ) QskGradient toGradient( const QJSValue& value )

View File

@ -56,24 +56,14 @@ static inline bool qskCanBeInterpolated( const QskGradient& from, const QskGradi
return from.type() == to.type(); 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 x = rect.x();
const qreal y = rect.y(); const qreal y = rect.y();
const qreal w = rect.width(); const qreal w = rect.width();
const qreal h = rect.height(); 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 ) QskGradient::QskGradient( const QColor& color )
@ -129,8 +119,7 @@ QskGradient::QskGradient( const QGradient& qGradient )
m_values[0] = g->focalPoint().x(); m_values[0] = g->focalPoint().x();
m_values[1] = g->focalPoint().y(); m_values[1] = g->focalPoint().y();
m_values[2] = g->focalRadius(); m_values[3] = m_values[2] = g->focalRadius();
m_values[3] = 0.0;
break; break;
} }
@ -258,7 +247,7 @@ void QskGradient::updateStatusBits() const
case Radial: case Radial:
{ {
m_isVisible = m_values[2] > 0.0; // radius m_isVisible = m_values[2] > 0.0 || m_values[3] > 0.0; // radius
break; 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] ); transform.map( m_values[0], m_values[1], &m_values[0], &m_values[1] );
#if 1 qreal rx = qMax( m_values[2], 0.0 );
m_values[2] *= qMin( rect.width(), rect.height() ); qreal ry = qMax( m_values[3], 0.0 );
#endif
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; break;
} }
@ -617,14 +622,18 @@ void QskGradient::setRadialDirection( const qreal x, qreal y, qreal radius )
setRadialDirection( QskRadialDirection( x, y, 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 ) void QskGradient::setRadialDirection( const QskRadialDirection& direction )
{ {
m_type = Radial; m_type = Radial;
m_values[0] = direction.center().x(); m_values[0] = direction.center().x();
m_values[1] = direction.center().y(); m_values[1] = direction.center().y();
m_values[2] = direction.radius(); m_values[2] = direction.radiusX();
m_values[3] = 0.0; m_values[3] = direction.radiusY();
} }
QskRadialDirection QskGradient::radialDirection() const QskRadialDirection QskGradient::radialDirection() const
@ -634,7 +643,7 @@ QskRadialDirection QskGradient::radialDirection() const
if ( m_type != Radial ) if ( m_type != Radial )
return QskRadialDirection( 0.5, 0.5, 0.0 ); 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 ) void QskGradient::setConicDirection( qreal x, qreal y )
@ -785,7 +794,7 @@ QDebug operator<<( QDebug debug, const QskGradient& gradient )
const auto dir = gradient.radialDirection(); const auto dir = gradient.radialDirection();
debug << dir.center().x() << "," << dir.center().y() debug << dir.center().x() << "," << dir.center().y()
<< "," << dir.radius() << ")"; << "," << dir.radiusX() << dir.radiusY() << ")";
break; 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"; debug << " SS";
break;
case QskGradient::StretchToHeight:
debug << " SH";
break;
case QskGradient::StretchToWidth:
debug << " SW";
break;
} }
switch( gradient.spreadMode() ) switch( gradient.spreadMode() )

View File

@ -61,10 +61,6 @@ class QSK_EXPORT QskGradient
enum StretchMode enum StretchMode
{ {
NoStretch, NoStretch,
StretchToHeight,
StretchToWidth,
StretchToSize StretchToSize
}; };
Q_ENUM( StretchMode ) Q_ENUM( StretchMode )
@ -98,6 +94,7 @@ class QSK_EXPORT QskGradient
void setRadialDirection( const QskRadialDirection& ); void setRadialDirection( const QskRadialDirection& );
void setRadialDirection( const qreal x, qreal y, qreal radius ); void setRadialDirection( const qreal x, qreal y, qreal radius );
void setRadialDirection( const qreal x, qreal y, qreal radiusX, qreal radiusY );
QskRadialDirection radialDirection() const; QskRadialDirection radialDirection() const;
void setConicDirection( qreal, qreal ); void setConicDirection( qreal, qreal );
@ -174,7 +171,7 @@ class QSK_EXPORT QskGradient
/* /*
Linear: x1, y1, x2, y2 Linear: x1, y1, x2, y2
Radial: centerX, centerY, radius, n/a Radial: centerX, centerY, radiusX, radiusY
Conic: centerX, centerY, startAngle, spanAngle Conic: centerX, centerY, startAngle, spanAngle
*/ */
qreal m_values[4] = {}; qreal m_values[4] = {};

View File

@ -142,9 +142,25 @@ void QskRadialDirection::setY( qreal y ) noexcept
m_y = y; 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 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" #include "moc_QskGradientDirection.cpp"

View File

@ -113,16 +113,18 @@ class QSK_EXPORT QskRadialDirection
Q_PROPERTY( qreal x READ x WRITE setX ) Q_PROPERTY( qreal x READ x WRITE setX )
Q_PROPERTY( qreal y READ y WRITE setY ) 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: public:
constexpr QskRadialDirection() noexcept = default; constexpr QskRadialDirection() noexcept = default;
constexpr QskRadialDirection( const QPointF& center, qreal radius ) noexcept; constexpr QskRadialDirection( const QPointF& center, qreal radius ) noexcept;
constexpr QskRadialDirection( qreal cx, qreal cy, 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; constexpr QPointF center() const noexcept;
void setCenter(const QPointF& center) noexcept; void setCenter(const QPointF& ) noexcept;
void setCenter(qreal x, qreal y) noexcept; void setCenter(qreal x, qreal y) noexcept;
void setX( qreal ) noexcept; void setX( qreal ) noexcept;
@ -131,13 +133,20 @@ class QSK_EXPORT QskRadialDirection
void setY( qreal ) noexcept; void setY( qreal ) noexcept;
constexpr qreal y() const noexcept; constexpr qreal y() const noexcept;
constexpr qreal radius() const noexcept; constexpr qreal radiusX() const noexcept;
void setRadius( qreal radius ) noexcept; void setRadiusX( qreal ) noexcept;
constexpr qreal radiusY() const noexcept;
void setRadiusY( qreal ) noexcept;
void setRadius( qreal ) noexcept;
void setRadius( qreal, qreal ) noexcept;
private: private:
qreal m_x = 0.5; qreal m_x = 0.5;
qreal m_y = 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( inline constexpr QskLinearDirection::QskLinearDirection(
@ -270,7 +279,17 @@ inline constexpr QskRadialDirection::QskRadialDirection(
qreal x, qreal y, qreal radius ) noexcept qreal x, qreal y, qreal radius ) noexcept
: m_x( x ) : m_x( x )
, m_y( y ) , 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; 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 #endif

View File

@ -70,7 +70,7 @@ namespace
virtual QSGMaterialShader* createShader() const = 0; virtual QSGMaterialShader* createShader() const = 0;
#endif #endif
virtual bool setGradient( const QRectF&, const QskGradient& ) = 0; virtual bool setGradient( const QskGradient& ) = 0;
}; };
#ifdef SHADER_GL #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; bool changed = false;
@ -194,11 +194,8 @@ namespace
const auto dir = gradient.linearDirection(); const auto dir = gradient.linearDirection();
const QVector4D vector( const QVector4D vector( dir.x1(), dir.y1(),
rect.left() + dir.x1() * rect.width(), dir.x2() - dir.x1(), dir.y2() - dir.y1() );
rect.top() + dir.y1() * rect.height(),
( dir.x2() - dir.x1() ) * rect.width(),
( dir.y2() - dir.y1() ) * rect.height() );
if ( m_gradientVector != vector ) if ( m_gradientVector != vector )
{ {
@ -333,7 +330,7 @@ namespace
return &type; return &type;
} }
bool setGradient( const QRectF& rect, const QskGradient& gradient ) override bool setGradient( const QskGradient& gradient ) override
{ {
bool changed = false; bool changed = false;
@ -351,12 +348,8 @@ namespace
const auto dir = gradient.radialDirection(); const auto dir = gradient.radialDirection();
const QVector2D pos( const QVector2D pos( dir.x(), dir.y() );
rect.left() + dir.center().x() * rect.width(), const QVector2D radius( dir.radiusX(), dir.radiusY() );
rect.top() + dir.center().y() * rect.height() );
const QVector2D radius( dir.radius() * rect.width(),
dir.radius() * rect.height() );
if ( ( pos != m_center ) || ( m_radius != radius ) ) if ( ( pos != m_center ) || ( m_radius != radius ) )
{ {
@ -504,7 +497,7 @@ namespace
return &type; return &type;
} }
bool setGradient( const QRectF& rect, const QskGradient& gradient ) override bool setGradient( const QskGradient& gradient ) override
{ {
bool changed = false; bool changed = false;
@ -522,9 +515,7 @@ namespace
const auto dir = gradient.conicDirection(); const auto dir = gradient.conicDirection();
const QVector2D center( const QVector2D center( dir.x(), dir.y() );
rect.left() + dir.center().x() * rect.width(),
rect.top() + dir.center().y() * rect.height() );
if ( center != m_center ) if ( center != m_center )
{ {
@ -715,7 +706,7 @@ bool QskGradientMaterial::updateGradient( const QRectF& rect, const QskGradient&
case QskGradient::Conic: case QskGradient::Conic:
{ {
auto material = static_cast< GradientMaterial* >( this ); auto material = static_cast< GradientMaterial* >( this );
return material->setGradient( rect, gradient ); return material->setGradient( gradient.stretchedTo( rect ) );
} }
default: default: