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->setFocalY( dir.y() );
g->setCenterRadius( dir.radius() );
g->setCenterRadius( qMax( dir.radiusX(), dir.radiusY() ) );
shapeGradient = g;
break;

View File

@ -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

View File

@ -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 );

View File

@ -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: [

View File

@ -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 )

View File

@ -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() )

View File

@ -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] = {};

View File

@ -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"

View File

@ -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

View File

@ -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: