working towards improved QskGradient
This commit is contained in:
parent
03ce740b29
commit
1f28eec9de
@ -52,108 +52,12 @@ static inline bool qskIsGradientValid( const QskGradientStops& stops )
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline QColor qskInterpolated(
|
||||
const QskGradientStop& s1, const QskGradientStop& s2, qreal pos )
|
||||
static inline bool qskCanBeInterpolated( const QskGradient& from, const QskGradient& to )
|
||||
{
|
||||
if ( s1.color() == s2.color() )
|
||||
return s1.color();
|
||||
|
||||
const qreal ratio = ( pos - s1.position() ) / ( s2.position() - s1.position() );
|
||||
return QskRgb::interpolated( s1.color(), s2.color(), ratio );
|
||||
}
|
||||
|
||||
static inline bool qskComparePositions(
|
||||
const QskGradientStops& s1, const QskGradientStops& s2 )
|
||||
{
|
||||
if ( s1.count() != s2.count() )
|
||||
return false;
|
||||
|
||||
// the first is always at 0.0, the last at 1.0
|
||||
for ( int i = 1; i < s1.count() - 1; i++ )
|
||||
{
|
||||
if ( s1[ i ].position() != s2[ i ].position() )
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( from.isMonochrome() || to.isMonochrome() )
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline QskGradientStops qskExpandedStops(
|
||||
const QskGradientStops& s1, const QskGradientStops& s2 )
|
||||
{
|
||||
// expand s1 by stops matching to the positions from s2
|
||||
|
||||
if ( qskComparePositions( s1, s2 ) )
|
||||
return s1;
|
||||
|
||||
QskGradientStops stops;
|
||||
|
||||
stops += s1.first();
|
||||
|
||||
int i = 1, j = 1;
|
||||
while ( ( i < s1.count() - 1 ) || ( j < s2.count() - 1 ) )
|
||||
{
|
||||
if ( s1[ i ].position() < s2[ j ].position() )
|
||||
{
|
||||
stops += s1[ i++ ];
|
||||
}
|
||||
else
|
||||
{
|
||||
const qreal pos = s2[ j++ ].position();
|
||||
stops += QskGradientStop( pos, qskInterpolated( s1[ i - 1 ], s1[ i ], pos ) );
|
||||
}
|
||||
}
|
||||
|
||||
stops += s1.last();
|
||||
|
||||
return stops;
|
||||
}
|
||||
|
||||
static inline QskGradientStops qskExtractedStops(
|
||||
const QskGradientStops& stops, qreal from, qreal to )
|
||||
{
|
||||
QskGradientStops extracted;
|
||||
|
||||
if ( from == to )
|
||||
extracted.reserve( 2 );
|
||||
else
|
||||
extracted.reserve( stops.size() );
|
||||
|
||||
int i = 0;
|
||||
|
||||
if ( from == 0.0 )
|
||||
{
|
||||
extracted += QskGradientStop( 0.0, stops[i++].color() );
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( i = 1; i < stops.count(); i++ )
|
||||
{
|
||||
if ( stops[i].position() > from )
|
||||
break;
|
||||
}
|
||||
|
||||
const auto color =
|
||||
QskGradientStop::interpolated( stops[i - 1], stops[i], from );
|
||||
|
||||
extracted += QskGradientStop( 0.0, color );
|
||||
}
|
||||
|
||||
for ( ; i < stops.count(); i++ )
|
||||
{
|
||||
const auto& s = stops[i];
|
||||
|
||||
if ( s.position() >= to )
|
||||
break;
|
||||
|
||||
const auto pos = ( s.position() - from ) / ( to - from );
|
||||
extracted += QskGradientStop( pos, s.color() );
|
||||
}
|
||||
|
||||
const auto color = QskGradientStop::interpolated( stops[i - 1], stops[i], to );
|
||||
extracted += QskGradientStop( 1.0, color );
|
||||
|
||||
return extracted;
|
||||
return from.orientation() == to.orientation();
|
||||
}
|
||||
|
||||
QskGradient::QskGradient( Orientation orientation ) noexcept
|
||||
@ -271,23 +175,13 @@ void QskGradient::setOrientation( Orientation orientation ) noexcept
|
||||
|
||||
void QskGradient::setStops( const QColor& color )
|
||||
{
|
||||
m_stops.clear();
|
||||
m_stops.reserve( 2 );
|
||||
|
||||
m_stops.append( QskGradientStop( 0.0, color ) );
|
||||
m_stops.append( QskGradientStop( 1.0, color ) );
|
||||
|
||||
m_stops = { { 0.0, color }, { 1.0, color } };
|
||||
m_isDirty = true;
|
||||
}
|
||||
|
||||
void QskGradient::setStops( const QColor& startColor, const QColor& stopColor )
|
||||
void QskGradient::setStops( const QColor& color1, const QColor& color2 )
|
||||
{
|
||||
m_stops.clear();
|
||||
m_stops.reserve( 2 );
|
||||
|
||||
m_stops.append( QskGradientStop( 0.0, startColor ) );
|
||||
m_stops.append( QskGradientStop( 1.0, stopColor ) );
|
||||
|
||||
m_stops = { { 0.0, color1 }, { 1.0, color2 } };
|
||||
m_isDirty = true;
|
||||
}
|
||||
|
||||
@ -377,158 +271,75 @@ QskGradient QskGradient::reversed() const
|
||||
|
||||
QskGradient QskGradient::extracted( qreal from, qreal to ) const
|
||||
{
|
||||
if ( from > to )
|
||||
return QskGradient( m_orientation );
|
||||
|
||||
if ( isMonochrome() || ( from <= 0.0 && to >= 1.0 ) )
|
||||
return *this;
|
||||
auto gradient = *this;
|
||||
|
||||
if ( !isValid() || ( from > to ) || ( from > 1.0 ) )
|
||||
{
|
||||
gradient.clearStops();
|
||||
}
|
||||
else if ( isMonochrome() )
|
||||
{
|
||||
from = qMax( from, 0.0 );
|
||||
to = qMin( to, 1.0 );
|
||||
|
||||
const auto stops = qskExtractedStops( m_stops, from, to );
|
||||
return QskGradient( orientation(), stops );
|
||||
const auto color = m_stops.first().color();
|
||||
|
||||
gradient.setStops( { { from, color }, { to, color } } );
|
||||
}
|
||||
else
|
||||
{
|
||||
gradient.setStops( qskExtractedGradientStops( m_stops, from, to ) );
|
||||
}
|
||||
|
||||
QskGradient QskGradient::interpolated(
|
||||
const QskGradient& to, qreal value ) const
|
||||
{
|
||||
if ( !( isValid() && to.isValid() ) )
|
||||
return gradient;
|
||||
}
|
||||
|
||||
QskGradient QskGradient::interpolated( const QskGradient& to, qreal ratio ) const
|
||||
{
|
||||
if ( !isValid() && !to.isValid() )
|
||||
return to;
|
||||
|
||||
qreal progress;
|
||||
const QskGradient* gradient;
|
||||
QskGradient gradient;
|
||||
|
||||
if ( to.isValid() )
|
||||
if ( qskCanBeInterpolated( *this, to ) )
|
||||
{
|
||||
progress = value;
|
||||
gradient = &to;
|
||||
}
|
||||
else
|
||||
{
|
||||
progress = 1.0 - value;
|
||||
gradient = this;
|
||||
}
|
||||
// We simply interpolate stops
|
||||
|
||||
/*
|
||||
We interpolate as if the invalid gradient would be
|
||||
a transparent version of the valid gradient
|
||||
*/
|
||||
gradient.setOrientation( to.orientation() );
|
||||
|
||||
auto stops = gradient->m_stops;
|
||||
for ( auto& stop : stops )
|
||||
{
|
||||
auto c = stop.color();
|
||||
c.setAlpha( c.alpha() * progress );
|
||||
|
||||
stop.setColor( c );
|
||||
}
|
||||
|
||||
return QskGradient( gradient->orientation(), stops );
|
||||
}
|
||||
|
||||
if ( isMonochrome() && to.isMonochrome() )
|
||||
{
|
||||
const auto c = QskRgb::interpolated(
|
||||
m_stops[ 0 ].color(), to.m_stops[ 0 ].color(), value );
|
||||
|
||||
return QskGradient( to.orientation(), c, c );
|
||||
}
|
||||
|
||||
if ( isMonochrome() )
|
||||
{
|
||||
// we can ignore our stops
|
||||
|
||||
const auto c = m_stops[ 0 ].color();
|
||||
|
||||
auto s2 = to.m_stops;
|
||||
for ( int i = 0; i < s2.count(); i++ )
|
||||
{
|
||||
const auto c2 = QskRgb::interpolated( c, s2[ i ].color(), value );
|
||||
s2[ i ].setColor( c2 );
|
||||
}
|
||||
|
||||
return QskGradient( to.orientation(), s2 );
|
||||
}
|
||||
|
||||
if ( to.isMonochrome() )
|
||||
{
|
||||
// we can ignore the stops of to
|
||||
|
||||
const auto c = to.m_stops[ 0 ].color();
|
||||
|
||||
auto s2 = m_stops;
|
||||
for ( int i = 0; i < s2.count(); i++ )
|
||||
{
|
||||
const auto c2 = QskRgb::interpolated( s2[ i ].color(), c, value );
|
||||
s2[ i ].setColor( c2 );
|
||||
}
|
||||
|
||||
return QskGradient( orientation(), s2 );
|
||||
}
|
||||
|
||||
if ( m_orientation == to.m_orientation )
|
||||
{
|
||||
/*
|
||||
we need to have the same number of stops
|
||||
at the same positions
|
||||
*/
|
||||
|
||||
const auto s1 = qskExpandedStops( m_stops, to.m_stops );
|
||||
auto s2 = qskExpandedStops( to.m_stops, m_stops );
|
||||
|
||||
for ( int i = 0; i < s1.count(); i++ )
|
||||
{
|
||||
const auto c2 = QskRgb::interpolated(
|
||||
s1[ i ].color(), s2[ i ].color(), value );
|
||||
|
||||
s2[ i ].setColor( c2 );
|
||||
}
|
||||
|
||||
return QskGradient( orientation(), s2 );
|
||||
gradient.setStops( qskInterpolatedGradientStops(
|
||||
m_stops, isMonochrome(),
|
||||
to.m_stops, to.isMonochrome(), ratio ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
The interpolation is devided into 2 steps. First we
|
||||
interpolate into a monochrome gradient and then change
|
||||
the orientation before we continue in direction of the
|
||||
final gradient.
|
||||
interpolate into a monochrome gradient and then
|
||||
recolor the gradient towards the target gradient
|
||||
This will always result in a smooth transition - even, when
|
||||
interpolating between different gradient types
|
||||
*/
|
||||
|
||||
const auto c = m_stops[ 0 ].color();
|
||||
const auto c = QskRgb::interpolated( startColor(), to.startColor(), 0.5 );
|
||||
|
||||
if ( value <= 0.5 )
|
||||
if ( ratio < 0.5 )
|
||||
{
|
||||
auto s2 = m_stops;
|
||||
const auto r = 2.0 * ratio;
|
||||
|
||||
for ( int i = 0; i < s2.count(); i++ )
|
||||
{
|
||||
const auto c2 = QskRgb::interpolated(
|
||||
s2[ i ].color(), c, 2 * value );
|
||||
|
||||
s2[ i ].setColor( c2 );
|
||||
}
|
||||
|
||||
return QskGradient( orientation(), s2 );
|
||||
gradient.setOrientation( orientation() );
|
||||
gradient.setStops( qskInterpolatedGradientStops( m_stops, c, r ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
auto s2 = to.m_stops;
|
||||
const auto r = 2.0 * ( ratio - 0.5 );
|
||||
|
||||
for ( int i = 0; i < s2.count(); i++ )
|
||||
{
|
||||
const auto c2 = QskRgb::interpolated(
|
||||
c, s2[ i ].color(), 2 * ( value - 0.5 ) );
|
||||
|
||||
s2[ i ].setColor( c2 );
|
||||
}
|
||||
|
||||
return QskGradient( to.orientation(), s2 );
|
||||
gradient.setOrientation( to.orientation() );
|
||||
gradient.setStops( qskInterpolatedGradientStops( c, to.m_stops, r ) );
|
||||
}
|
||||
}
|
||||
|
||||
return gradient;
|
||||
}
|
||||
|
||||
QVariant QskGradient::interpolate(
|
||||
|
@ -6,7 +6,6 @@
|
||||
#ifndef QSK_GRADIENT_H
|
||||
#define QSK_GRADIENT_H
|
||||
|
||||
#include "QskGlobal.h"
|
||||
#include "QskGradientStop.h"
|
||||
|
||||
#include <qbrush.h>
|
||||
@ -88,14 +87,14 @@ class QSK_EXPORT QskGradient
|
||||
void reverse();
|
||||
QskGradient reversed() const;
|
||||
|
||||
// all stops between [from, to] with positions streched into [0,1]
|
||||
QskGradient extracted( qreal from, qreal start ) const;
|
||||
|
||||
QskGradient interpolated( const QskGradient&, qreal value ) const;
|
||||
|
||||
static QVariant interpolate( const QskGradient&,
|
||||
const QskGradient&, qreal progress );
|
||||
|
||||
// all stops between [from, to] with positions streched into [0,1]
|
||||
QskGradient extracted( qreal from, qreal start ) const;
|
||||
|
||||
QskHashValue hash( QskHashValue seed ) const;
|
||||
|
||||
Q_INVOKABLE qreal stopAt( int index ) const noexcept;
|
||||
@ -136,7 +135,7 @@ inline QskGradient::QskGradient( QGradient::Preset preset )
|
||||
|
||||
inline bool QskGradient::operator!=( const QskGradient& other ) const noexcept
|
||||
{
|
||||
return ( !( *this == other ) );
|
||||
return !( *this == other );
|
||||
}
|
||||
|
||||
inline QskGradient::Orientation QskGradient::orientation() const noexcept
|
||||
|
@ -98,6 +98,15 @@ QDebug operator<<( QDebug debug, const QskGradientStop& stop )
|
||||
|
||||
// some helper functions around QskGradientStops
|
||||
|
||||
static inline QColor qskInterpolatedColor(
|
||||
const QskGradientStops& stops, int index1, int index2, qreal position )
|
||||
{
|
||||
index1 = qBound( 0, index1, stops.count() - 1 );
|
||||
index2 = qBound( 0, index2, stops.count() - 1 );
|
||||
|
||||
return QskGradientStop::interpolated( stops[ index1 ], stops[ index2 ], position );
|
||||
}
|
||||
|
||||
bool qskIsVisible( const QskGradientStops& stops ) noexcept
|
||||
{
|
||||
for ( const auto& stop : stops )
|
||||
@ -133,9 +142,175 @@ QskGradientStops qskTransparentGradientStops( const QskGradientStops& stops, qre
|
||||
stop.setColor( c );
|
||||
}
|
||||
|
||||
return stops;
|
||||
}
|
||||
|
||||
QskGradientStops qskInterpolatedGradientStops(
|
||||
const QskGradientStops& stops, const QColor& color, qreal ratio )
|
||||
{
|
||||
auto newStops = stops;
|
||||
|
||||
for ( auto& stop : newStops )
|
||||
stop.setColor( QskRgb::interpolated( stop.color(), color, ratio ) );
|
||||
|
||||
return newStops;
|
||||
}
|
||||
|
||||
QskGradientStops qskInterpolatedGradientStops(
|
||||
const QColor& color, const QskGradientStops& stops, qreal ratio )
|
||||
{
|
||||
auto newStops = stops;
|
||||
|
||||
for ( auto& stop : newStops )
|
||||
stop.setColor( QskRgb::interpolated( color, stop.color(), ratio ) );
|
||||
|
||||
return newStops;
|
||||
}
|
||||
|
||||
static QskGradientStops qskInterpolatedStops(
|
||||
const QskGradientStops& from, const QskGradientStops& to, qreal ratio )
|
||||
{
|
||||
/*
|
||||
We have to insert interpolated stops for all positions
|
||||
that can be found in s1 and s2. So in case there is no
|
||||
stop at a specific position of the other stops we
|
||||
have to calculate one temporarily before interpolating.
|
||||
*/
|
||||
QVector< QskGradientStop > stops;
|
||||
|
||||
int i = 0, j = 0;
|
||||
while ( ( i < from.count() ) || ( j < to.count() ) )
|
||||
{
|
||||
qreal pos;
|
||||
QColor c1, c2;
|
||||
|
||||
if ( i == from.count() )
|
||||
{
|
||||
c1 = from.last().color();
|
||||
c2 = to[j].color();
|
||||
pos = to[j++].position();
|
||||
}
|
||||
else if ( j == to.count() )
|
||||
{
|
||||
c1 = from[i].color();
|
||||
c2 = to.last().color();
|
||||
pos = from[i++].position();
|
||||
}
|
||||
else
|
||||
{
|
||||
c1 = from[i].color();
|
||||
c2 = to[j].color();
|
||||
|
||||
const qreal dpos = from[i].position() - to[j].position();
|
||||
|
||||
if ( qFuzzyIsNull( dpos ) )
|
||||
{
|
||||
pos = from[i++].position();
|
||||
j++;
|
||||
}
|
||||
else if ( dpos < 0.0 )
|
||||
{
|
||||
pos = from[i++].position();
|
||||
|
||||
if ( j > 0 )
|
||||
{
|
||||
const auto& stop = to[j - 1];
|
||||
c2 = QskRgb::interpolated( stop.color(), c2, pos - stop.position() );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = to[j++].position();
|
||||
|
||||
if ( i > 0 )
|
||||
{
|
||||
const auto& stop = from[i - 1];
|
||||
c1 = QskRgb::interpolated( stop.color(), c1, pos - stop.position() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stops += QskGradientStop( pos, QskRgb::interpolated( c1, c2, ratio ) );
|
||||
}
|
||||
|
||||
return stops;
|
||||
}
|
||||
|
||||
QskGradientStops qskInterpolatedGradientStops(
|
||||
const QskGradientStops& from, bool fromIsMonochrome,
|
||||
const QskGradientStops& to, bool toIsMonochrome, qreal ratio )
|
||||
{
|
||||
if ( from.isEmpty() && to.isEmpty() )
|
||||
return QskGradientStops();
|
||||
|
||||
if ( from.isEmpty() )
|
||||
return qskTransparentGradientStops( to, ratio );
|
||||
|
||||
if ( to.isEmpty() )
|
||||
return qskTransparentGradientStops( from, 1.0 - ratio );
|
||||
|
||||
if ( fromIsMonochrome && toIsMonochrome )
|
||||
{
|
||||
const auto c = QskRgb::interpolated(
|
||||
from[ 0 ].color(), to[ 0 ].color(), ratio );
|
||||
|
||||
return { { 0.0, c }, { 1.0, c } };
|
||||
}
|
||||
|
||||
if ( fromIsMonochrome )
|
||||
{
|
||||
return qskInterpolatedGradientStops( from[ 0 ].color(), to, ratio );
|
||||
}
|
||||
|
||||
if ( toIsMonochrome )
|
||||
{
|
||||
return qskInterpolatedGradientStops( from, to[ 0 ].color(), ratio );
|
||||
}
|
||||
|
||||
return qskInterpolatedStops( from, to, ratio );
|
||||
}
|
||||
|
||||
QskGradientStops qskExtractedGradientStops(
|
||||
const QskGradientStops& stops, qreal from, qreal to )
|
||||
{
|
||||
if ( ( from > to ) || ( from > 1.0 ) || stops.isEmpty() )
|
||||
return QskGradientStops();
|
||||
|
||||
if ( ( from <= 0.0 ) && ( to >= 1.0 ) )
|
||||
return stops;
|
||||
|
||||
from = qMax( from, 0.0 );
|
||||
to = qMin( to, 1.0 );
|
||||
|
||||
QVector< QskGradientStop > extracted;
|
||||
extracted.reserve( stops.count() );
|
||||
|
||||
int i = 0;
|
||||
|
||||
for ( ; i < stops.count(); i++ )
|
||||
{
|
||||
if ( stops[i].position() > from )
|
||||
break;
|
||||
}
|
||||
|
||||
extracted += QskGradientStop( 0.0,
|
||||
qskInterpolatedColor( stops, i - 1, i, from ) );
|
||||
|
||||
for ( ; i < stops.count(); i++ )
|
||||
{
|
||||
if ( stops[i].position() >= to )
|
||||
break;
|
||||
|
||||
const auto pos = ( stops[i].position() - from ) / ( to - from );
|
||||
extracted += QskGradientStop( pos, stops[i].color() );
|
||||
}
|
||||
|
||||
extracted += QskGradientStop( 1.0,
|
||||
qskInterpolatedColor( stops, i, i + 1, to ) );
|
||||
|
||||
return extracted;
|
||||
}
|
||||
|
||||
QVector< QskGradientStop > qskBuildGradientStops( const QGradientStops& qtStops )
|
||||
{
|
||||
QVector< QskGradientStop > stops;
|
||||
|
@ -127,6 +127,22 @@ typedef QVector< QskGradientStop > QskGradientStops;
|
||||
QSK_EXPORT bool qskIsMonochrome( const QskGradientStops& ) noexcept;
|
||||
QSK_EXPORT bool qskIsVisible( const QskGradientStops& ) noexcept;
|
||||
|
||||
QSK_EXPORT QskGradientStops qskInterpolatedGradientStops(
|
||||
const QskGradientStops&, bool, const QskGradientStops&, bool,
|
||||
qreal ratio );
|
||||
|
||||
QSK_EXPORT QskGradientStops qskInterpolatedGradientStops(
|
||||
const QskGradientStops&, const QColor&, qreal ratio );
|
||||
|
||||
QSK_EXPORT QskGradientStops qskInterpolatedGradientStops(
|
||||
const QColor&, const QskGradientStops&, qreal ratio );
|
||||
|
||||
QSK_EXPORT QskGradientStops qskTransparentGradientStops(
|
||||
const QskGradientStops&, qreal ratio );
|
||||
|
||||
QSK_EXPORT QskGradientStops qskExtractedGradientStops(
|
||||
const QskGradientStops&, qreal from, qreal to );
|
||||
|
||||
QSK_EXPORT QskGradientStops qskBuildGradientStops(
|
||||
const QVector< QRgb >&, bool discrete = false );
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user