diff --git a/src/common/QskGradient.cpp b/src/common/QskGradient.cpp index 17c99557..bcfb6375 100644 --- a/src/common/QskGradient.cpp +++ b/src/common/QskGradient.cpp @@ -81,7 +81,7 @@ QskGradient::QskGradient( const QColor& color1, const QColor& color2 ) QskGradient::QskGradient( QGradient::Preset preset ) : QskGradient() { - setStops( qskBuildGradientStops( QGradient( preset ).stops() ) ); + setStops( qskFromQGradientStops( QGradient( preset ).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 @@ -305,7 +305,7 @@ void QskGradient::setStops( const QColor& color1, const QColor& color2 ) void QskGradient::setStops( QGradient::Preset preset ) { - const auto stops = qskBuildGradientStops( QGradient( preset ).stops() ); + const auto stops = qskFromQGradientStops( QGradient( preset ).stops() ); setStops( stops ); } diff --git a/src/common/QskGradientStop.cpp b/src/common/QskGradientStop.cpp index 82d59d06..1a3411b6 100644 --- a/src/common/QskGradientStop.cpp +++ b/src/common/QskGradientStop.cpp @@ -23,6 +23,31 @@ static void 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 { m_position = position; @@ -56,26 +81,19 @@ QskHashValue QskGradientStop::hash( QskHashValue seed ) const noexcept return qHashBits( &m_color, sizeof( m_color ), hash ); } -QColor QskGradientStop::interpolated( - const QskGradientStop& s1, const QskGradientStop& s2, qreal position ) noexcept +QskGradientStop QskGradientStop::interpolated( + const QskGradientStop& to, qreal ratio ) const { - if ( s1.color() == s2.color() ) - return s1.color(); + return QskGradientStop( + m_position + ( to.m_position - m_position ) * ratio, + QskRgb::interpolated( m_color, to.m_color, ratio ) + ); +} - auto min = &s1; - auto max = &s2; - - if ( min->position() > max->position() ) - 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 ); +QVariant QskGradientStop::interpolate( + const QskGradientStop& from, const QskGradientStop& to, qreal ratio ) +{ + return QVariant::fromValue( from.interpolated( to, ratio ) ); } #ifndef QT_NO_DEBUG_STREAM @@ -95,19 +113,23 @@ QDebug operator<<( QDebug debug, const QskGradientStop& stop ) #endif -#include "moc_QskGradientStop.cpp" - // some helper functions around QskGradientStops -static inline QColor qskInterpolatedColor( - const QskGradientStops& stops, int index1, int index2, qreal position ) +static inline QColor qskColorAtPosition( + 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 ); - index2 = qBound( 0, index2, max ); + return QskRgb::interpolated( + 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 @@ -278,43 +300,126 @@ QColor qskInterpolatedColorAt( const QskGradientStops& stops, qreal pos ) noexce if ( stops.isEmpty() ) return QColor(); - pos = qBound( 0.0, pos, 1.0 ); - if ( pos <= stops.first().position() ) return stops.first().color(); for ( int i = 1; i < stops.count(); i++ ) { - if ( pos <= stops[i].position() ) - return qskInterpolatedColor( stops, i - 1, i, pos ); + if ( pos <= stops[ i ].position() ) + return qskColorAtPosition( stops[ i - 1 ], stops[ i ], pos ); } return stops.last().color(); } -QskGradientStops qskExtractedGradientStops( - const QskGradientStops& gradientStops, qreal from, qreal to ) +QskGradientStops qskReplacedGradientStops( const QskGradientStops& gradientStops, + const QskGradientStop& stop1, const QskGradientStop& stop2 ) { - if ( ( from > to ) || ( from > 1.0 ) || gradientStops.isEmpty() ) - return QskGradientStops(); - - if ( ( from <= 0.0 ) && ( to >= 1.0 ) ) + if ( stop1.position() >= stop2.position() ) return gradientStops; - from = qMax( from, 0.0 ); - to = qMin( to, 1.0 ); + const auto s1 = QskGradientStop( qskBoundedStopPos( stop1.position() ), stop1.color() ); + const auto s2 = QskGradientStop( qskBoundedStopPos( stop2.position() ), stop2.color() ); - QVector< QskGradientStop > stops1 = gradientStops; + QskGradientStops stops; -#if 1 - // not the most efficient implementation - maybe later TODO ... + if ( s1.position() == 0.0 && s2.position() == 1.0 ) + { + stops = { s1, s2 }; + } + else if ( qskIsMonochrome( gradientStops ) ) + { + stops.reserve( 4 ); - if ( stops1.first().position() > 0.0 ) - stops1.prepend( QskGradientStop( 0.0, stops1.first().color() ) ); + const auto c = gradientStops.isEmpty() + ? QColor::fromRgba( 0 ) : gradientStops.first().color(); - if ( stops1.last().position() < 1.0 ) - stops1.append( QskGradientStop( 1.0, stops1.last().color() ) ); -#endif + if ( s1.position() > 0.0 ) + stops += { s1.position(), c }; + + 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; stops2.reserve( stops1.count() ); @@ -331,26 +436,26 @@ QskGradientStops qskExtractedGradientStops( { int i = 0; - for ( ; i < stops1.count(); i++ ) + if ( from == 0.0 ) { - if ( stops1[i].position() > from ) - break; + stops2 += stops1[i++]; + } + else + { + while( stops1[++i].position() <= from ); // skip leading stops + + stops2 += QskGradientStop( 0.0, + qskColorAtPosition( stops1[i - 1], stops1[ i ], from ) ); } - stops2 += QskGradientStop( 0.0, - qskInterpolatedColor( stops1, i - 1, i, from ) ); - - for ( ; i < stops1.count(); i++ ) + while ( stops1[i].position() < to ) { - if ( stops1[i].position() >= to ) - break; - 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, - qskInterpolatedColor( stops1, i, i + 1, to ) ); + qskColorAtPosition( stops1[i - 1], stops1[ i ], to ) ); } return stops2; @@ -367,7 +472,7 @@ QskGradientStops qskRevertedGradientStops( const QskGradientStops& stops ) return s; } -QVector< QskGradientStop > qskBuildGradientStops( const QGradientStops& qtStops ) +QVector< QskGradientStop > qskFromQGradientStops( const QGradientStops& qtStops ) { QVector< QskGradientStop > stops; stops.reserve( qtStops.count() ); @@ -463,3 +568,5 @@ QGradientStops qskToQGradientStops( const QskGradientStops& stops ) return qStops; } + +#include "moc_QskGradientStop.cpp" diff --git a/src/common/QskGradientStop.h b/src/common/QskGradientStop.h index ae6bed02..a8625c7d 100644 --- a/src/common/QskGradientStop.h +++ b/src/common/QskGradientStop.h @@ -45,8 +45,10 @@ class QSK_EXPORT QskGradientStop void setRgb( QRgb ) noexcept; QRgb rgb() const noexcept; - static QColor interpolated( - const QskGradientStop&, const QskGradientStop&, qreal position ) noexcept; + QskGradientStop interpolated( const QskGradientStop&, qreal ) const; + + static QVariant interpolate( const QskGradientStop&, + const QskGradientStop&, qreal ); QskHashValue hash( QskHashValue seed ) const noexcept; @@ -129,27 +131,48 @@ QSK_EXPORT QskGradientStops qskInterpolatedGradientStops( const QskGradientStops&, bool, const QskGradientStops&, bool, qreal ratio ); +// interpolating colors in direction of a color. QSK_EXPORT QskGradientStops qskInterpolatedGradientStops( const QskGradientStops&, const QColor&, qreal ratio ); +// interpolating colors starting from a color. QSK_EXPORT QskGradientStops qskInterpolatedGradientStops( const QColor&, const QskGradientStops&, qreal ratio ); +// interpolating the opacity of the colors QSK_EXPORT QskGradientStops qskTransparentGradientStops( const QskGradientStops&, qreal ratio ); +// extracting the colors of [from, to ] and stretching them to [0.0, 1.0] QSK_EXPORT QskGradientStops qskExtractedGradientStops( 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( const QVector< QRgb >&, bool discrete = false ); QSK_EXPORT QskGradientStops qskBuildGradientStops( const QVector< QColor >&, bool discrete = false ); -QSK_EXPORT QskGradientStops qskRevertedGradientStops( const QskGradientStops& ); - -QSK_EXPORT QskGradientStops qskBuildGradientStops( const QVector< QGradientStop >& ); +/* + convert color stops from/to a vector of QGradientStop, that can be + used for QGradients. + */ +QSK_EXPORT QskGradientStops qskFromQGradientStops( const QVector< QGradientStop >& ); QSK_EXPORT QVector< QGradientStop > qskToQGradientStops( const QVector< QskGradientStop >& ); #endif diff --git a/src/controls/QskVariantAnimator.cpp b/src/controls/QskVariantAnimator.cpp index 631fae2f..45431e09 100644 --- a/src/controls/QskVariantAnimator.cpp +++ b/src/controls/QskVariantAnimator.cpp @@ -13,6 +13,7 @@ #include "QskGraduationMetrics.h" #include "QskColorFilter.h" #include "QskGradient.h" +#include "QskGradientStop.h" #include "QskMargins.h" #include "QskIntervalF.h" #include "QskTextColors.h" @@ -43,6 +44,7 @@ static void qskRegisterInterpolator() qRegisterAnimationInterpolator< QskColorFilter >( QskColorFilter::interpolate ); qRegisterAnimationInterpolator< QskIntervalF >( QskIntervalF::interpolate ); qRegisterAnimationInterpolator< QskMargins >( QskMargins::interpolate ); + qRegisterAnimationInterpolator< QskGradientStop >( QskGradientStop::interpolate ); qRegisterAnimationInterpolator< QskGradient >( QskGradient::interpolate ); qRegisterAnimationInterpolator< QskBoxShapeMetrics >( QskBoxShapeMetrics::interpolate ); qRegisterAnimationInterpolator< QskBoxBorderMetrics >( QskBoxBorderMetrics::interpolate );