from master

This commit is contained in:
Uwe Rathmann 2025-05-15 11:46:15 +02:00
parent cc5229f7a3
commit 7305502de0
4 changed files with 199 additions and 67 deletions

View File

@ -81,7 +81,7 @@ QskGradient::QskGradient( const QColor& color1, const QColor& color2 )
QskGradient::QskGradient( QGradient::Preset preset ) QskGradient::QskGradient( QGradient::Preset preset )
: QskGradient() : QskGradient()
{ {
setStops( qskBuildGradientStops( QGradient( preset ).stops() ) ); setStops( qskFromQGradientStops( QGradient( preset ).stops() ) );
} }
QskGradient::QskGradient( const QVector< QskGradientStop >& stops ) QskGradient::QskGradient( const QVector< QskGradientStop >& stops )
@ -165,7 +165,7 @@ QskGradient::QskGradient( const QGradient& qGradient )
} }
} }
setStops( qskBuildGradientStops( qGradient.stops() ) ); setStops( qskFromQGradientStops( qGradient.stops() ) );
} }
QskGradient::QskGradient( const QskGradient& other ) noexcept QskGradient::QskGradient( const QskGradient& other ) noexcept
@ -305,7 +305,7 @@ void QskGradient::setStops( const QColor& color1, const QColor& color2 )
void QskGradient::setStops( QGradient::Preset preset ) void QskGradient::setStops( QGradient::Preset preset )
{ {
const auto stops = qskBuildGradientStops( QGradient( preset ).stops() ); const auto stops = qskFromQGradientStops( QGradient( preset ).stops() );
setStops( stops ); setStops( stops );
} }

View File

@ -23,6 +23,31 @@ static void qskRegisterGradientStop()
Q_CONSTRUCTOR_FUNCTION( qskRegisterGradientStop ) Q_CONSTRUCTOR_FUNCTION( qskRegisterGradientStop )
static inline qreal qskBoundedStopPos( qreal pos )
{
if ( ( pos < 0.0 ) || qFuzzyIsNull( pos ) )
return 0.0;
if ( ( pos > 1.0 ) || qFuzzyCompare( pos, 1.0 ) )
return 1.0;
return pos;
}
static inline QVector< QskGradientStop >
qskNormalizedStops( const QVector< QskGradientStop >& stops )
{
auto s = stops;
if ( s.first().position() > 0.0 )
s.prepend( QskGradientStop( 0.0, s.first().color() ) );
if ( s.last().position() < 1.0 )
s.append( QskGradientStop( 1.0, s.last().color() ) );
return s;
}
void QskGradientStop::setPosition( qreal position ) noexcept void QskGradientStop::setPosition( qreal position ) noexcept
{ {
m_position = position; m_position = position;
@ -56,26 +81,19 @@ QskHashValue QskGradientStop::hash( QskHashValue seed ) const noexcept
return qHashBits( &m_color, sizeof( m_color ), hash ); return qHashBits( &m_color, sizeof( m_color ), hash );
} }
QColor QskGradientStop::interpolated( QskGradientStop QskGradientStop::interpolated(
const QskGradientStop& s1, const QskGradientStop& s2, qreal position ) noexcept const QskGradientStop& to, qreal ratio ) const
{ {
if ( s1.color() == s2.color() ) return QskGradientStop(
return s1.color(); m_position + ( to.m_position - m_position ) * ratio,
QskRgb::interpolated( m_color, to.m_color, ratio )
);
}
auto min = &s1; QVariant QskGradientStop::interpolate(
auto max = &s2; const QskGradientStop& from, const QskGradientStop& to, qreal ratio )
{
if ( min->position() > max->position() ) return QVariant::fromValue( from.interpolated( to, ratio ) );
std::swap( min, max );
if ( position <= min->position() )
return min->color();
if ( position >= max->position() )
return max->color();
const qreal r = ( position - min->position() ) / ( max->position() - min->position() );
return QskRgb::interpolated( min->color(), max->color(), r );
} }
#ifndef QT_NO_DEBUG_STREAM #ifndef QT_NO_DEBUG_STREAM
@ -95,19 +113,23 @@ QDebug operator<<( QDebug debug, const QskGradientStop& stop )
#endif #endif
#include "moc_QskGradientStop.cpp"
// some helper functions around QskGradientStops // some helper functions around QskGradientStops
static inline QColor qskInterpolatedColor( static inline QColor qskColorAtPosition(
const QskGradientStops& stops, int index1, int index2, qreal position ) const QskGradientStop& s1, const QskGradientStop& s2, qreal pos )
{ {
const auto max = static_cast< int >( stops.count() ) - 1; const auto dp = s2.position() - s1.position();
if ( qFuzzyIsNull( dp ) )
return s1.color();
index1 = qBound( 0, index1, max ); return QskRgb::interpolated(
index2 = qBound( 0, index2, max ); s1.color(), s2.color(), ( pos - s1.position() ) / dp );
}
return QskGradientStop::interpolated( stops[ index1 ], stops[ index2 ], position ); static inline QskGradientStop qskCreateStopAtPosition(
const QskGradientStop& s1, const QskGradientStop& s2, qreal pos )
{
return { pos, qskColorAtPosition( s1, s2, pos ) };
} }
bool qskIsVisible( const QskGradientStops& stops ) noexcept bool qskIsVisible( const QskGradientStops& stops ) noexcept
@ -278,43 +300,126 @@ QColor qskInterpolatedColorAt( const QskGradientStops& stops, qreal pos ) noexce
if ( stops.isEmpty() ) if ( stops.isEmpty() )
return QColor(); return QColor();
pos = qBound( 0.0, pos, 1.0 );
if ( pos <= stops.first().position() ) if ( pos <= stops.first().position() )
return stops.first().color(); return stops.first().color();
for ( int i = 1; i < stops.count(); i++ ) for ( int i = 1; i < stops.count(); i++ )
{ {
if ( pos <= stops[i].position() ) if ( pos <= stops[ i ].position() )
return qskInterpolatedColor( stops, i - 1, i, pos ); return qskColorAtPosition( stops[ i - 1 ], stops[ i ], pos );
} }
return stops.last().color(); return stops.last().color();
} }
QskGradientStops qskExtractedGradientStops( QskGradientStops qskReplacedGradientStops( const QskGradientStops& gradientStops,
const QskGradientStops& gradientStops, qreal from, qreal to ) const QskGradientStop& stop1, const QskGradientStop& stop2 )
{ {
if ( ( from > to ) || ( from > 1.0 ) || gradientStops.isEmpty() ) if ( stop1.position() >= stop2.position() )
return QskGradientStops();
if ( ( from <= 0.0 ) && ( to >= 1.0 ) )
return gradientStops; return gradientStops;
from = qMax( from, 0.0 ); const auto s1 = QskGradientStop( qskBoundedStopPos( stop1.position() ), stop1.color() );
to = qMin( to, 1.0 ); const auto s2 = QskGradientStop( qskBoundedStopPos( stop2.position() ), stop2.color() );
QVector< QskGradientStop > stops1 = gradientStops; QskGradientStops stops;
#if 1 if ( s1.position() == 0.0 && s2.position() == 1.0 )
// not the most efficient implementation - maybe later TODO ... {
stops = { s1, s2 };
}
else if ( qskIsMonochrome( gradientStops ) )
{
stops.reserve( 4 );
if ( stops1.first().position() > 0.0 ) const auto c = gradientStops.isEmpty()
stops1.prepend( QskGradientStop( 0.0, stops1.first().color() ) ); ? QColor::fromRgba( 0 ) : gradientStops.first().color();
if ( stops1.last().position() < 1.0 ) if ( s1.position() > 0.0 )
stops1.append( QskGradientStop( 1.0, stops1.last().color() ) ); stops += { s1.position(), c };
#endif
stops += s1;
stops += s2;
if ( s2.position() < 1.0 )
stops += { s2.position(), c };
}
else
{
// not the most efficient implementation - maybe later TODO ...
const auto stops0 = qskNormalizedStops( gradientStops );
int i = 0;
if ( s1.position() > 0.0 )
{
while ( s1.position() > stops0[i].position() )
stops += stops0[i++];
if ( s1.position() == stops0[i].position() )
stops += stops0[i++];
else
stops += qskCreateStopAtPosition( stops0[i - 1], stops0[i], s1.position() );
}
stops += s1;
while ( s2.position() > stops0[i].position() )
i++;
stops += s2;
if ( s2.position() < 1.0 )
{
while ( stops0[i + 1].position() == s2.position() )
i++;
if ( s2.position() != stops0[i].position() )
stops += qskCreateStopAtPosition( stops0[i - 1], stops0[i], s2.position() );
while( i < stops0.count() )
stops += stops0[i++];
}
}
return stops;
}
QskGradientStops qskClippedGradientStops(
const QskGradientStops& stops, qreal from, qreal to )
{
return qskReplacedGradientStops( stops, { from, 0 }, { to, 0 } );
}
QskGradientStops qskExtractedGradientStops(
const QskGradientStops& stops, qreal from, qreal to )
{
if ( ( from > to ) || ( to > 1.0 ) || ( from < 0.0 ) || stops.isEmpty() )
return QskGradientStops();
from = qskBoundedStopPos( from );
to = qskBoundedStopPos( to );
if ( ( from == 0.0 ) && ( to == 1.0 ) )
return stops;
if ( from == to )
{
const auto color = qskInterpolatedColorAt( stops, from );
QVector< QskGradientStop > s;
s.reserve( 2 );
s += QskGradientStop( 0.0, color );
s += QskGradientStop( 1.0, color );
return s;
}
/*
For situations where we have no stops at 0.0 and 1.0 we insert them
manually. Not the most efficient implementation, but we avoid having
to deal with these situations for the moment. TODO ...
*/
const auto stops1 = qskNormalizedStops( stops );
QVector< QskGradientStop > stops2; QVector< QskGradientStop > stops2;
stops2.reserve( stops1.count() ); stops2.reserve( stops1.count() );
@ -331,26 +436,26 @@ QskGradientStops qskExtractedGradientStops(
{ {
int i = 0; int i = 0;
for ( ; i < stops1.count(); i++ ) if ( from == 0.0 )
{ {
if ( stops1[i].position() > from ) stops2 += stops1[i++];
break; }
else
{
while( stops1[++i].position() <= from ); // skip leading stops
stops2 += QskGradientStop( 0.0,
qskColorAtPosition( stops1[i - 1], stops1[ i ], from ) );
} }
stops2 += QskGradientStop( 0.0, while ( stops1[i].position() < to )
qskInterpolatedColor( stops1, i - 1, i, from ) );
for ( ; i < stops1.count(); i++ )
{ {
if ( stops1[i].position() >= to )
break;
const auto pos = ( stops1[i].position() - from ) / ( to - from ); const auto pos = ( stops1[i].position() - from ) / ( to - from );
stops2 += QskGradientStop( pos, stops1[i].color() ); stops2 += QskGradientStop( pos, stops1[i++].color() );
} }
stops2 += QskGradientStop( 1.0, stops2 += QskGradientStop( 1.0,
qskInterpolatedColor( stops1, i, i + 1, to ) ); qskColorAtPosition( stops1[i - 1], stops1[ i ], to ) );
} }
return stops2; return stops2;
@ -367,7 +472,7 @@ QskGradientStops qskRevertedGradientStops( const QskGradientStops& stops )
return s; return s;
} }
QVector< QskGradientStop > qskBuildGradientStops( const QGradientStops& qtStops ) QVector< QskGradientStop > qskFromQGradientStops( const QGradientStops& qtStops )
{ {
QVector< QskGradientStop > stops; QVector< QskGradientStop > stops;
stops.reserve( qtStops.count() ); stops.reserve( qtStops.count() );
@ -463,3 +568,5 @@ QGradientStops qskToQGradientStops( const QskGradientStops& stops )
return qStops; return qStops;
} }
#include "moc_QskGradientStop.cpp"

View File

@ -45,8 +45,10 @@ class QSK_EXPORT QskGradientStop
void setRgb( QRgb ) noexcept; void setRgb( QRgb ) noexcept;
QRgb rgb() const noexcept; QRgb rgb() const noexcept;
static QColor interpolated( QskGradientStop interpolated( const QskGradientStop&, qreal ) const;
const QskGradientStop&, const QskGradientStop&, qreal position ) noexcept;
static QVariant interpolate( const QskGradientStop&,
const QskGradientStop&, qreal );
QskHashValue hash( QskHashValue seed ) const noexcept; QskHashValue hash( QskHashValue seed ) const noexcept;
@ -129,27 +131,48 @@ QSK_EXPORT QskGradientStops qskInterpolatedGradientStops(
const QskGradientStops&, bool, const QskGradientStops&, bool, const QskGradientStops&, bool, const QskGradientStops&, bool,
qreal ratio ); qreal ratio );
// interpolating colors in direction of a color.
QSK_EXPORT QskGradientStops qskInterpolatedGradientStops( QSK_EXPORT QskGradientStops qskInterpolatedGradientStops(
const QskGradientStops&, const QColor&, qreal ratio ); const QskGradientStops&, const QColor&, qreal ratio );
// interpolating colors starting from a color.
QSK_EXPORT QskGradientStops qskInterpolatedGradientStops( QSK_EXPORT QskGradientStops qskInterpolatedGradientStops(
const QColor&, const QskGradientStops&, qreal ratio ); const QColor&, const QskGradientStops&, qreal ratio );
// interpolating the opacity of the colors
QSK_EXPORT QskGradientStops qskTransparentGradientStops( QSK_EXPORT QskGradientStops qskTransparentGradientStops(
const QskGradientStops&, qreal ratio ); const QskGradientStops&, qreal ratio );
// extracting the colors of [from, to ] and stretching them to [0.0, 1.0]
QSK_EXPORT QskGradientStops qskExtractedGradientStops( QSK_EXPORT QskGradientStops qskExtractedGradientStops(
const QskGradientStops&, qreal from, qreal to ); const QskGradientStops&, qreal from, qreal to );
// reverting the color stops
QSK_EXPORT QskGradientStops qskRevertedGradientStops( const QskGradientStops& );
QSK_EXPORT QskGradientStops qskReplacedGradientStops(
const QskGradientStops&, const QskGradientStop&, const QskGradientStop& );
QSK_EXPORT QskGradientStops qskClippedGradientStops(
const QskGradientStops&, qreal from, qreal to );
/*
creating equidistant color stops from a list of colors.
when discrete is true the result will contain 2 stops at each position
one with the previous and one with the following color so that the
interval [pos1-pos2] will be monochrome.
*/
QSK_EXPORT QskGradientStops qskBuildGradientStops( QSK_EXPORT QskGradientStops qskBuildGradientStops(
const QVector< QRgb >&, bool discrete = false ); const QVector< QRgb >&, bool discrete = false );
QSK_EXPORT QskGradientStops qskBuildGradientStops( QSK_EXPORT QskGradientStops qskBuildGradientStops(
const QVector< QColor >&, bool discrete = false ); const QVector< QColor >&, bool discrete = false );
QSK_EXPORT QskGradientStops qskRevertedGradientStops( const QskGradientStops& ); /*
convert color stops from/to a vector of QGradientStop, that can be
QSK_EXPORT QskGradientStops qskBuildGradientStops( const QVector< QGradientStop >& ); used for QGradients.
*/
QSK_EXPORT QskGradientStops qskFromQGradientStops( const QVector< QGradientStop >& );
QSK_EXPORT QVector< QGradientStop > qskToQGradientStops( const QVector< QskGradientStop >& ); QSK_EXPORT QVector< QGradientStop > qskToQGradientStops( const QVector< QskGradientStop >& );
#endif #endif

View File

@ -13,6 +13,7 @@
#include "QskGraduationMetrics.h" #include "QskGraduationMetrics.h"
#include "QskColorFilter.h" #include "QskColorFilter.h"
#include "QskGradient.h" #include "QskGradient.h"
#include "QskGradientStop.h"
#include "QskMargins.h" #include "QskMargins.h"
#include "QskIntervalF.h" #include "QskIntervalF.h"
#include "QskTextColors.h" #include "QskTextColors.h"
@ -43,6 +44,7 @@ static void qskRegisterInterpolator()
qRegisterAnimationInterpolator< QskColorFilter >( QskColorFilter::interpolate ); qRegisterAnimationInterpolator< QskColorFilter >( QskColorFilter::interpolate );
qRegisterAnimationInterpolator< QskIntervalF >( QskIntervalF::interpolate ); qRegisterAnimationInterpolator< QskIntervalF >( QskIntervalF::interpolate );
qRegisterAnimationInterpolator< QskMargins >( QskMargins::interpolate ); qRegisterAnimationInterpolator< QskMargins >( QskMargins::interpolate );
qRegisterAnimationInterpolator< QskGradientStop >( QskGradientStop::interpolate );
qRegisterAnimationInterpolator< QskGradient >( QskGradient::interpolate ); qRegisterAnimationInterpolator< QskGradient >( QskGradient::interpolate );
qRegisterAnimationInterpolator< QskBoxShapeMetrics >( QskBoxShapeMetrics::interpolate ); qRegisterAnimationInterpolator< QskBoxShapeMetrics >( QskBoxShapeMetrics::interpolate );
qRegisterAnimationInterpolator< QskBoxBorderMetrics >( QskBoxBorderMetrics::interpolate ); qRegisterAnimationInterpolator< QskBoxBorderMetrics >( QskBoxBorderMetrics::interpolate );