6#include "QskGradient.h"
7#include "QskRgbValue.h"
8#include "QskGradientDirection.h"
9#include "QskFunctions.h"
14static void qskRegisterGradient()
16 qRegisterMetaType< QskGradient >();
18#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
19 QMetaType::registerEqualsComparator< QskGradient >();
22 QMetaType::registerConverter< QColor, QskGradient >(
23 [](
const QColor& color ) {
return QskGradient( color ); } );
26Q_CONSTRUCTOR_FUNCTION( qskRegisterGradient )
28static inline bool qskIsGradientValid(
const QskGradientStops& stops )
30 if ( stops.isEmpty() )
33 if ( stops.first().position() < 0.0 || stops.last().position() > 1.0 )
36 if ( !stops.first().color().isValid() )
39 for (
int i = 1; i < stops.size(); i++ )
41 if ( stops[ i ].position() < stops[ i - 1 ].position() )
44 if ( !stops[ i ].color().isValid() )
53 if ( from.isMonochrome() || to.isMonochrome() )
56 return from.type() == to.type();
59static inline QTransform qskTransformForRect(
int,
const QRectF& rect )
61 const qreal x = rect.x();
62 const qreal y = rect.y();
63 const qreal w = rect.width();
64 const qreal h = rect.height();
66 return QTransform( w, 0, 0, h, x, y );
69QskGradient::QskGradient(
const QColor& color )
75QskGradient::QskGradient(
const QColor& color1,
const QColor& color2 )
78 setStops( color1, color2 );
81QskGradient::QskGradient( QGradient::Preset preset )
84 setStops( qskBuildGradientStops( QGradient( preset ).stops() ) );
87QskGradient::QskGradient(
const QVector< QskGradientStop >& stops )
93QskGradient::QskGradient(
const QGradient& qGradient )
96 switch( qGradient.type() )
98 case QGradient::LinearGradient:
102 const auto g =
static_cast< const QLinearGradient*
>( &qGradient );
104 m_values[0] = g->start().x();
105 m_values[1] = g->start().y();
106 m_values[2] = g->finalStop().x();
107 m_values[3] = g->finalStop().y();
111 case QGradient::RadialGradient:
115 const auto g =
static_cast< const QRadialGradient*
>( &qGradient );
117 if ( ( g->center() != g->focalPoint() ) || ( g->radius() != g->focalRadius() ) )
118 qWarning() <<
"QskGradient: extended radial gradients are not supported.";
120 m_values[0] = g->focalPoint().x();
121 m_values[1] = g->focalPoint().y();
122 m_values[3] = m_values[2] = g->focalRadius();
126 case QGradient::ConicalGradient:
130 const auto g =
static_cast< const QConicalGradient*
>( &qGradient );
132 m_values[0] = g->center().x();
133 m_values[1] = g->center().y();
134 m_values[2] = g->angle();
146 m_spreadMode =
static_cast< SpreadMode
>( qGradient.spread() );
148 switch( qGradient.coordinateMode() )
150 case QGradient::ObjectMode:
151 case QGradient::ObjectBoundingMode:
153 m_stretchMode = StretchToSize;
156 case QGradient::LogicalMode:
158 m_stretchMode = NoStretch;
161 case QGradient::StretchToDeviceMode:
163 qWarning() <<
"QskGradient: StretchToDeviceMode is not supportd.";
164 m_stretchMode = NoStretch;
168 setStops( qskBuildGradientStops( qGradient.stops() ) );
171QskGradient::QskGradient(
const QskGradient& other ) noexcept
172 : m_stops( other.m_stops )
173 , m_values{ other.m_values[0], other.m_values[1],
174 other.m_values[2], other.m_values[3], other.m_values[4] }
175 , m_type( other.m_type )
176 , m_spreadMode( other.m_spreadMode )
177 , m_stretchMode( other.m_stretchMode )
178 , m_isDirty( other.m_isDirty )
179 , m_isValid( other.m_isValid )
180 , m_isMonchrome( other.m_isMonchrome )
181 , m_isVisible( other.m_isVisible )
185QskGradient::~QskGradient()
191 m_type = other.m_type;
192 m_spreadMode = other.m_spreadMode;
193 m_stretchMode = other.m_stretchMode;
194 m_stops = other.m_stops;
196 m_values[0] = other.m_values[0];
197 m_values[1] = other.m_values[1];
198 m_values[2] = other.m_values[2];
199 m_values[3] = other.m_values[3];
200 m_values[4] = other.m_values[4];
202 m_isDirty = other.m_isDirty;
203 m_isValid = other.m_isValid;
204 m_isMonchrome = other.m_isMonchrome;
205 m_isVisible = other.m_isVisible;
210bool QskGradient::operator==(
const QskGradient& other )
const noexcept
212 return ( m_type == other.m_type )
213 && ( m_spreadMode == other.m_spreadMode )
214 && ( m_stretchMode == other.m_stretchMode )
215 && ( m_values[0] == other.m_values[0] )
216 && ( m_values[1] == other.m_values[1] )
217 && ( m_values[2] == other.m_values[2] )
218 && ( m_values[3] == other.m_values[3] )
219 && ( m_values[4] == other.m_values[4] )
220 && ( m_stops == other.m_stops );
223void QskGradient::updateStatusBits()
const
226 m_isValid = qskIsGradientValid( m_stops );
230 m_isMonchrome = qskIsMonochrome( m_stops );
231 m_isVisible = qskIsVisible( m_stops );
235 m_isMonchrome =
true;
245 m_isVisible = !( qskFuzzyCompare( m_values[0], m_values[2] )
246 && qskFuzzyCompare( m_values[1], m_values[3] ) );
252 m_isVisible = m_values[2] > 0.0 || m_values[3] > 0.0;
258 m_isVisible = !qFuzzyIsNull( m_values[3] );
270bool QskGradient::isValid() const noexcept
278bool QskGradient::isMonochrome() const noexcept
283 return m_isMonchrome;
286bool QskGradient::isVisible() const noexcept
294void QskGradient::setStops(
const QColor& color )
296 m_stops = { { 0.0, color }, { 1.0, color } };
300void QskGradient::setStops(
const QColor& color1,
const QColor& color2 )
302 m_stops = { { 0.0, color1 }, { 1.0, color2 } };
306void QskGradient::setStops( QGradient::Preset preset )
308 const auto stops = qskBuildGradientStops( QGradient( preset ).stops() );
312void QskGradient::setStops(
const QskGradientStops& stops )
314 if ( !stops.isEmpty() && !qskIsGradientValid( stops ) )
316 qWarning(
"Invalid gradient stops" );
327int QskGradient::stepCount() const noexcept
332 auto steps =
static_cast< int >( m_stops.count() ) - 1;
334 if ( m_stops.first().position() > 0.0 )
337 if ( m_stops.last().position() < 1.0 )
343qreal QskGradient::stopAt(
int index )
const noexcept
345 if ( index < 0 || index >= m_stops.size() )
348 return m_stops[ index ].position();
351bool QskGradient::hasStopAt( qreal value )
const noexcept
354 for (
auto& stop : m_stops )
356 if ( stop.position() == value )
359 if ( stop.position() > value )
366QColor QskGradient::colorAt(
int index )
const noexcept
368 if ( index >= m_stops.size() )
371 return m_stops[ index ].color();
374void QskGradient::setAlpha(
int alpha )
376 for (
auto& stop : m_stops )
378 auto c = stop.color();
379 if ( c.isValid() && c.alpha() )
389void QskGradient::setSpreadMode( SpreadMode spreadMode )
391 m_spreadMode = spreadMode;
394void QskGradient::setStretchMode( StretchMode stretchMode )
396 m_stretchMode = stretchMode;
399void QskGradient::stretchTo(
const QRectF& rect )
401 if ( m_stretchMode == NoStretch || m_type == Stops || rect.isEmpty() )
404 const auto transform = qskTransformForRect( m_stretchMode, rect );
406 switch(
static_cast< int >( m_type ) )
410 transform.map( m_values[0], m_values[1], &m_values[0], &m_values[1] );
411 transform.map( m_values[2], m_values[3], &m_values[2], &m_values[3] );
417 transform.map( m_values[0], m_values[1], &m_values[0], &m_values[1] );
419 qreal rx = qMax( m_values[2], 0.0 );
420 qreal ry = qMax( m_values[3], 0.0 );
422 if ( rx == 0.0 || ry == 0.0 )
430 const qreal r = qMin( rect.width(), rect.height() ) * qMax( rx, ry );
431 m_values[2] = m_values[3] = r;
435 m_values[2] = rx * rect.width();
436 m_values[3] = ry * rect.height();
443 transform.map( m_values[0], m_values[1], &m_values[0], &m_values[1] );
445 if ( m_values[4] == 0.0 && !rect.isEmpty() )
446 m_values[4] = rect.width() / rect.height();
452 m_stretchMode = NoStretch;
455QskGradient QskGradient::stretchedTo(
const QSizeF& size )
const
457 return stretchedTo( QRectF( 0.0, 0.0, size.width(), size.height() ) );
460QskGradient QskGradient::stretchedTo(
const QRectF& rect )
const
462 if ( m_stretchMode == NoStretch )
471void QskGradient::reverse()
473 if ( isMonochrome() )
476 std::reverse( m_stops.begin(), m_stops.end() );
477 for(
auto& stop : m_stops )
478 stop.setPosition( 1.0 - stop.position() );
483 auto gradient = *
this;
489QskGradient QskGradient::extracted( qreal from, qreal to )
const
491 auto gradient = *
this;
493 if ( !isValid() || ( from > to ) || ( from > 1.0 ) )
495 gradient.clearStops();
497 else if ( isMonochrome() )
499 from = qMax( from, 0.0 );
500 to = qMin( to, 1.0 );
502 const auto color = m_stops.first().color();
504 gradient.setStops( { { from, color }, { to, color } } );
508 gradient.setStops( qskExtractedGradientStops( m_stops, from, to ) );
516 if ( !isValid() && !to.isValid() )
521 if ( qskCanBeInterpolated( *
this, to ) )
527 const auto stops = qskInterpolatedGradientStops(
528 m_stops, isMonochrome(), to.m_stops, to.isMonochrome(), ratio );
530 gradient.setStops( stops );
532 for ( uint i = 0; i <
sizeof( m_values ) /
sizeof( m_values[0] ); i++ )
533 gradient.m_values[i] = m_values[i] + ratio * ( to.m_values[i] - m_values[i] );
545 const auto c = QskRgb::interpolated( startColor(), to.startColor(), 0.5 );
549 const auto r = 2.0 * ratio;
552 gradient.setStops( qskInterpolatedGradientStops( m_stops, c, r ) );
556 const auto r = 2.0 * ( ratio - 0.5 );
559 gradient.setStops( qskInterpolatedGradientStops( c, to.m_stops, r ) );
566QVariant QskGradient::interpolate(
569 return QVariant::fromValue( from.interpolated( to, progress ) );
572void QskGradient::clearStops()
574 if ( !m_stops.isEmpty() )
581QskHashValue QskGradient::hash( QskHashValue seed )
const
583 auto hash = qHash( m_type, seed );
584 hash = qHash( m_spreadMode, seed );
585 hash = qHash( m_stretchMode, seed );
587 if ( m_type != Stops )
588 hash = qHashBits( m_values,
sizeof( m_values ), hash );
590 for (
const auto& stop : m_stops )
591 hash = stop.hash( hash );
596void QskGradient::setLinearDirection( Qt::Orientation orientation )
601void QskGradient::setLinearDirection( qreal x1, qreal y1, qreal x2, qreal y2 )
610 m_values[0] = direction.x1();
611 m_values[1] = direction.y1();
612 m_values[2] = direction.x2();
613 m_values[3] = direction.y2();
618 if ( m_type != Linear )
620 qWarning() <<
"Requesting linear attributes from a non linear gradient.";
627void QskGradient::setRadialDirection(
const qreal x, qreal y, qreal radius )
632void QskGradient::setRadialDirection(
const qreal x, qreal y,qreal radiusX, qreal radiusY )
640 m_values[0] = direction.center().x();
641 m_values[1] = direction.center().y();
642 m_values[2] = direction.radiusX();
643 m_values[3] = direction.radiusY();
648 if ( m_type != Radial )
650 qWarning() <<
"Requesting radial attributes from a non radial gradient.";
657void QskGradient::setConicDirection( qreal x, qreal y )
662void QskGradient::setConicDirection( qreal x, qreal y,
663 qreal startAngle, qreal spanAngle )
668void QskGradient::setConicDirection( qreal x, qreal y,
669 qreal startAngle, qreal spanAngle, qreal aspectRatio )
672 setConicDirection( dir );
679 m_values[0] = direction.center().x();
680 m_values[1] = direction.center().y();
681 m_values[2] = direction.startAngle();
682 m_values[3] = direction.spanAngle();
683 m_values[4] = direction.aspectRatio();
688 if ( m_type != Conic )
690 qWarning() <<
"Requesting conic attributes from a non conic gradient.";
695 dir.setAspectRatio( m_values[4] );
700void QskGradient::setDirection( Type type )
722void QskGradient::resetDirection()
725 m_values[0] = m_values[1] = m_values[2] = m_values[3] = m_values[4] = 0.0;
728QGradient QskGradient::toQGradient()
const
732 switch(
static_cast< int >( m_type ) )
736 g = QLinearGradient( m_values[0], m_values[1], m_values[2], m_values[3] );
742 g = QRadialGradient( m_values[0], m_values[1], m_values[2] );
748 if ( m_values[3] != 360.0 )
751 "QskGradient: spanAngle got lost, when converting to QConicalGradient";
754 g = QConicalGradient( m_values[0], m_values[1], m_values[2] );
759 g.setCoordinateMode( m_stretchMode == NoStretch
760 ? QGradient::LogicalMode : QGradient::ObjectMode );
762 g.setSpread(
static_cast< QGradient::Spread
>( m_spreadMode ) );
763 g.setStops( qskToQGradientStops( m_stops ) );
768#ifndef QT_NO_DEBUG_STREAM
772QDebug operator<<( QDebug debug,
const QskGradient& gradient )
774 QDebugStateSaver saver( debug );
777 debug <<
"QskGradient(";
779 switch ( gradient.type() )
781 case QskGradient::Linear:
785 const auto dir = gradient.linearDirection();
786 debug << dir.start().x() <<
"," << dir.start().y()
787 <<
"," << dir.stop().x() <<
"," << dir.stop().y() <<
")";
792 case QskGradient::Radial:
796 const auto dir = gradient.radialDirection();
798 debug << dir.center().x() <<
"," << dir.center().y()
799 <<
"," << dir.radiusX() << dir.radiusY() <<
")";
804 case QskGradient::Conic:
808 const auto dir = gradient.conicDirection();
810 debug << dir.center().x() <<
"," << dir.center().y()
811 <<
",AR:" << dir.aspectRatio()
812 <<
",[" << dir.startAngle() <<
"," << dir.spanAngle() <<
"])";
816 case QskGradient::Stops:
822 if ( !gradient.stops().isEmpty() )
824 if ( gradient.isMonochrome() )
827 QskRgb::debugColor( debug, gradient.rgbStart() );
833 const auto& stops = gradient.stops();
834 for (
int i = 0; i < stops.count(); i++ )
846 if ( gradient.stretchMode() == QskGradient::StretchToSize )
851 switch( gradient.spreadMode() )
853 case QskGradient::RepeatSpread:
857 case QskGradient::ReflectSpread:
861 case QskGradient::PadSpread:
872#include "moc_QskGradient.cpp"