870 lines
21 KiB
C++
870 lines
21 KiB
C++
/******************************************************************************
|
|
* QSkinny - Copyright (C) The authors
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*****************************************************************************/
|
|
|
|
#include "QskGradient.h"
|
|
#include "QskRgbValue.h"
|
|
#include "QskGradientDirection.h"
|
|
#include "QskFunctions.h"
|
|
|
|
#include <qvariant.h>
|
|
#include <qdebug.h>
|
|
|
|
static void qskRegisterGradient()
|
|
{
|
|
qRegisterMetaType< QskGradient >();
|
|
|
|
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
|
|
QMetaType::registerEqualsComparator< QskGradient >();
|
|
#endif
|
|
|
|
QMetaType::registerConverter< QColor, QskGradient >(
|
|
[]( const QColor& color ) { return QskGradient( color ); } );
|
|
}
|
|
|
|
Q_CONSTRUCTOR_FUNCTION( qskRegisterGradient )
|
|
|
|
static inline bool qskIsGradientValid( const QskGradientStops& stops )
|
|
{
|
|
if ( stops.isEmpty() )
|
|
return false;
|
|
|
|
if ( stops.first().position() < 0.0 || stops.last().position() > 1.0 )
|
|
return false;
|
|
|
|
if ( !stops.first().color().isValid() )
|
|
return false;
|
|
|
|
for ( int i = 1; i < stops.size(); i++ )
|
|
{
|
|
if ( stops[ i ].position() < stops[ i - 1 ].position() )
|
|
return false;
|
|
|
|
if ( !stops[ i ].color().isValid() )
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static inline bool qskCanBeInterpolated( const QskGradient& from, const QskGradient& to )
|
|
{
|
|
if ( from.isMonochrome() || to.isMonochrome() )
|
|
return true;
|
|
|
|
return from.type() == to.type();
|
|
}
|
|
|
|
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();
|
|
|
|
return QTransform( w, 0, 0, h, x, y );
|
|
}
|
|
|
|
QskGradient::QskGradient( const QColor& color )
|
|
: QskGradient()
|
|
{
|
|
setStops( color );
|
|
}
|
|
|
|
QskGradient::QskGradient( const QColor& color1, const QColor& color2 )
|
|
: QskGradient()
|
|
{
|
|
setStops( color1, color2 );
|
|
}
|
|
|
|
QskGradient::QskGradient( QGradient::Preset preset )
|
|
: QskGradient()
|
|
{
|
|
setStops( qskBuildGradientStops( QGradient( preset ).stops() ) );
|
|
}
|
|
|
|
QskGradient::QskGradient( const QVector< QskGradientStop >& stops )
|
|
: QskGradient()
|
|
{
|
|
setStops( stops );
|
|
}
|
|
|
|
QskGradient::QskGradient( const QGradient& qGradient )
|
|
: QskGradient()
|
|
{
|
|
switch( qGradient.type() )
|
|
{
|
|
case QGradient::LinearGradient:
|
|
{
|
|
m_type = Linear;
|
|
|
|
const auto g = static_cast< const QLinearGradient* >( &qGradient );
|
|
|
|
m_values[0] = g->start().x();
|
|
m_values[1] = g->start().y();
|
|
m_values[2] = g->finalStop().x();
|
|
m_values[3] = g->finalStop().y();
|
|
|
|
break;
|
|
}
|
|
case QGradient::RadialGradient:
|
|
{
|
|
m_type = Radial;
|
|
|
|
const auto g = static_cast< const QRadialGradient* >( &qGradient );
|
|
|
|
if ( ( g->center() != g->focalPoint() ) || ( g->radius() != g->focalRadius() ) )
|
|
qWarning() << "QskGradient: extended radial gradients are not supported.";
|
|
|
|
m_values[0] = g->focalPoint().x();
|
|
m_values[1] = g->focalPoint().y();
|
|
m_values[3] = m_values[2] = g->focalRadius();
|
|
|
|
break;
|
|
}
|
|
case QGradient::ConicalGradient:
|
|
{
|
|
m_type = Conic;
|
|
|
|
const auto g = static_cast< const QConicalGradient* >( &qGradient );
|
|
|
|
m_values[0] = g->center().x();
|
|
m_values[1] = g->center().y();
|
|
m_values[2] = g->angle();
|
|
m_values[3] = 360.0;
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
m_type = Stops;
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_spreadMode = static_cast< SpreadMode >( qGradient.spread() );
|
|
|
|
switch( qGradient.coordinateMode() )
|
|
{
|
|
case QGradient::ObjectMode:
|
|
case QGradient::ObjectBoundingMode:
|
|
|
|
m_stretchMode = StretchToSize;
|
|
break;
|
|
|
|
case QGradient::LogicalMode:
|
|
|
|
m_stretchMode = NoStretch;
|
|
break;
|
|
|
|
case QGradient::StretchToDeviceMode:
|
|
{
|
|
qWarning() << "QskGradient: StretchToDeviceMode is not supportd.";
|
|
m_stretchMode = NoStretch;
|
|
}
|
|
}
|
|
|
|
setStops( qskBuildGradientStops( qGradient.stops() ) );
|
|
}
|
|
|
|
QskGradient::QskGradient( const QskGradient& other ) noexcept
|
|
: m_stops( other.m_stops )
|
|
, m_values{ other.m_values[0], other.m_values[1],
|
|
other.m_values[2], other.m_values[3], other.m_values[4] }
|
|
, m_type( other.m_type )
|
|
, m_spreadMode( other.m_spreadMode )
|
|
, m_stretchMode( other.m_stretchMode )
|
|
, m_isDirty( other.m_isDirty )
|
|
, m_isValid( other.m_isValid )
|
|
, m_isMonchrome( other.m_isMonchrome )
|
|
, m_isVisible( other.m_isVisible )
|
|
{
|
|
}
|
|
|
|
QskGradient::~QskGradient()
|
|
{
|
|
}
|
|
|
|
QskGradient& QskGradient::operator=( const QskGradient& other ) noexcept
|
|
{
|
|
m_type = other.m_type;
|
|
m_spreadMode = other.m_spreadMode;
|
|
m_stretchMode = other.m_stretchMode;
|
|
m_stops = other.m_stops;
|
|
|
|
m_values[0] = other.m_values[0];
|
|
m_values[1] = other.m_values[1];
|
|
m_values[2] = other.m_values[2];
|
|
m_values[3] = other.m_values[3];
|
|
m_values[4] = other.m_values[4];
|
|
|
|
m_isDirty = other.m_isDirty;
|
|
m_isValid = other.m_isValid;
|
|
m_isMonchrome = other.m_isMonchrome;
|
|
m_isVisible = other.m_isVisible;
|
|
|
|
return *this;
|
|
}
|
|
|
|
bool QskGradient::operator==( const QskGradient& other ) const noexcept
|
|
{
|
|
return ( m_type == other.m_type )
|
|
&& ( m_spreadMode == other.m_spreadMode )
|
|
&& ( m_stretchMode == other.m_stretchMode )
|
|
&& ( m_values[0] == other.m_values[0] )
|
|
&& ( m_values[1] == other.m_values[1] )
|
|
&& ( m_values[2] == other.m_values[2] )
|
|
&& ( m_values[3] == other.m_values[3] )
|
|
&& ( m_values[4] == other.m_values[4] )
|
|
&& ( m_stops == other.m_stops );
|
|
}
|
|
|
|
void QskGradient::updateStatusBits() const
|
|
{
|
|
// doing all bits in one loop ?
|
|
m_isValid = qskIsGradientValid( m_stops );
|
|
|
|
if ( m_isValid )
|
|
{
|
|
m_isMonchrome = qskIsMonochrome( m_stops );
|
|
m_isVisible = qskIsVisible( m_stops );
|
|
}
|
|
else
|
|
{
|
|
m_isMonchrome = true;
|
|
m_isVisible = false;
|
|
}
|
|
|
|
if ( m_isVisible )
|
|
{
|
|
switch( m_type )
|
|
{
|
|
case Linear:
|
|
{
|
|
m_isVisible = !( qskFuzzyCompare( m_values[0], m_values[2] )
|
|
&& qskFuzzyCompare( m_values[1], m_values[3] ) );
|
|
break;
|
|
}
|
|
|
|
case Radial:
|
|
{
|
|
m_isVisible = m_values[2] > 0.0 || m_values[3] > 0.0; // radius
|
|
break;
|
|
}
|
|
|
|
case Conic:
|
|
{
|
|
m_isVisible = !qFuzzyIsNull( m_values[3] ); // spanAngle
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_isDirty = false;
|
|
}
|
|
|
|
bool QskGradient::isValid() const noexcept
|
|
{
|
|
if ( m_isDirty )
|
|
updateStatusBits();
|
|
|
|
return m_isValid;
|
|
}
|
|
|
|
bool QskGradient::isMonochrome() const noexcept
|
|
{
|
|
if ( m_isDirty )
|
|
updateStatusBits();
|
|
|
|
return m_isMonchrome;
|
|
}
|
|
|
|
bool QskGradient::isVisible() const noexcept
|
|
{
|
|
if ( m_isDirty )
|
|
updateStatusBits();
|
|
|
|
return m_isVisible;
|
|
}
|
|
|
|
void QskGradient::setStops( const QColor& color )
|
|
{
|
|
m_stops = { { 0.0, color }, { 1.0, color } };
|
|
m_isDirty = true;
|
|
}
|
|
|
|
void QskGradient::setStops( const QColor& color1, const QColor& color2 )
|
|
{
|
|
m_stops = { { 0.0, color1 }, { 1.0, color2 } };
|
|
m_isDirty = true;
|
|
}
|
|
|
|
void QskGradient::setStops( QGradient::Preset preset )
|
|
{
|
|
const auto stops = qskBuildGradientStops( QGradient( preset ).stops() );
|
|
setStops( stops );
|
|
}
|
|
|
|
void QskGradient::setStops( const QskGradientStops& stops )
|
|
{
|
|
if ( !stops.isEmpty() && !qskIsGradientValid( stops ) )
|
|
{
|
|
qWarning( "Invalid gradient stops" );
|
|
m_stops.clear();
|
|
}
|
|
else
|
|
{
|
|
m_stops = stops;
|
|
}
|
|
|
|
m_isDirty = true;
|
|
}
|
|
|
|
int QskGradient::stepCount() const noexcept
|
|
{
|
|
if ( !isValid() )
|
|
return 0;
|
|
|
|
auto steps = static_cast< int >( m_stops.count() ) - 1;
|
|
|
|
if ( m_stops.first().position() > 0.0 )
|
|
steps++;
|
|
|
|
if ( m_stops.last().position() < 1.0 )
|
|
steps++;
|
|
|
|
return steps;
|
|
}
|
|
|
|
qreal QskGradient::stopAt( int index ) const noexcept
|
|
{
|
|
if ( index < 0 || index >= m_stops.size() )
|
|
return -1.0;
|
|
|
|
return m_stops[ index ].position();
|
|
}
|
|
|
|
bool QskGradient::hasStopAt( qreal value ) const noexcept
|
|
{
|
|
// better use binary search TODO ...
|
|
for ( auto& stop : m_stops )
|
|
{
|
|
if ( stop.position() == value )
|
|
return true;
|
|
|
|
if ( stop.position() > value )
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
QColor QskGradient::colorAt( int index ) const noexcept
|
|
{
|
|
if ( index >= m_stops.size() )
|
|
return QColor();
|
|
|
|
return m_stops[ index ].color();
|
|
}
|
|
|
|
void QskGradient::setAlpha( int alpha )
|
|
{
|
|
for ( auto& stop : m_stops )
|
|
{
|
|
auto c = stop.color();
|
|
if ( c.isValid() && c.alpha() )
|
|
{
|
|
c.setAlpha( alpha );
|
|
stop.setColor( c );
|
|
}
|
|
}
|
|
|
|
m_isDirty = true;
|
|
}
|
|
|
|
void QskGradient::setSpreadMode( SpreadMode spreadMode )
|
|
{
|
|
m_spreadMode = spreadMode;
|
|
}
|
|
|
|
void QskGradient::setStretchMode( StretchMode stretchMode )
|
|
{
|
|
m_stretchMode = stretchMode;
|
|
}
|
|
|
|
void QskGradient::stretchTo( const QRectF& rect )
|
|
{
|
|
if ( m_stretchMode == NoStretch || m_type == Stops || rect.isEmpty() )
|
|
return; // nothing to do
|
|
|
|
const auto transform = qskTransformForRect( m_stretchMode, rect );
|
|
|
|
switch( static_cast< int >( m_type ) )
|
|
{
|
|
case Linear:
|
|
{
|
|
transform.map( m_values[0], m_values[1], &m_values[0], &m_values[1] );
|
|
transform.map( m_values[2], m_values[3], &m_values[2], &m_values[3] );
|
|
|
|
break;
|
|
}
|
|
case Radial:
|
|
{
|
|
transform.map( m_values[0], m_values[1], &m_values[0], &m_values[1] );
|
|
|
|
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;
|
|
}
|
|
case Conic:
|
|
{
|
|
transform.map( m_values[0], m_values[1], &m_values[0], &m_values[1] );
|
|
|
|
if ( m_values[4] == 0.0 && !rect.isEmpty() )
|
|
m_values[4] = rect.width() / rect.height();
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_stretchMode = NoStretch;
|
|
}
|
|
|
|
QskGradient QskGradient::stretchedTo( const QSizeF& size ) const
|
|
{
|
|
return stretchedTo( QRectF( 0.0, 0.0, size.width(), size.height() ) );
|
|
}
|
|
|
|
QskGradient QskGradient::stretchedTo( const QRectF& rect ) const
|
|
{
|
|
if ( m_stretchMode == NoStretch )
|
|
return *this;
|
|
|
|
QskGradient g = *this;
|
|
g.stretchTo( rect );
|
|
|
|
return g;
|
|
}
|
|
|
|
void QskGradient::reverse()
|
|
{
|
|
if ( isMonochrome() )
|
|
return;
|
|
|
|
std::reverse( m_stops.begin(), m_stops.end() );
|
|
for( auto& stop : m_stops )
|
|
stop.setPosition( 1.0 - stop.position() );
|
|
}
|
|
|
|
QskGradient QskGradient::reversed() const
|
|
{
|
|
auto gradient = *this;
|
|
gradient.reverse();
|
|
|
|
return gradient;
|
|
}
|
|
|
|
QskGradient QskGradient::extracted( qreal from, qreal to ) const
|
|
{
|
|
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 color = m_stops.first().color();
|
|
|
|
gradient.setStops( { { from, color }, { to, color } } );
|
|
}
|
|
else
|
|
{
|
|
gradient.setStops( qskExtractedGradientStops( m_stops, from, to ) );
|
|
}
|
|
|
|
return gradient;
|
|
}
|
|
|
|
QskGradient QskGradient::interpolated( const QskGradient& to, qreal ratio ) const
|
|
{
|
|
if ( !isValid() && !to.isValid() )
|
|
return to;
|
|
|
|
QskGradient gradient;
|
|
|
|
if ( qskCanBeInterpolated( *this, to ) )
|
|
{
|
|
// We simply interpolate stops and values
|
|
|
|
gradient = to;
|
|
|
|
const auto stops = qskInterpolatedGradientStops(
|
|
m_stops, isMonochrome(), to.m_stops, to.isMonochrome(), ratio );
|
|
|
|
gradient.setStops( stops );
|
|
|
|
for ( uint i = 0; i < sizeof( m_values ) / sizeof( m_values[0] ); i++ )
|
|
gradient.m_values[i] = m_values[i] + ratio * ( to.m_values[i] - m_values[i] );
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
The interpolation is devided into 2 steps. First we
|
|
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 = QskRgb::interpolated( startColor(), to.startColor(), 0.5 );
|
|
|
|
if ( ratio < 0.5 )
|
|
{
|
|
const auto r = 2.0 * ratio;
|
|
|
|
gradient = *this;
|
|
gradient.setStops( qskInterpolatedGradientStops( m_stops, c, r ) );
|
|
}
|
|
else
|
|
{
|
|
const auto r = 2.0 * ( ratio - 0.5 );
|
|
|
|
gradient = to;
|
|
gradient.setStops( qskInterpolatedGradientStops( c, to.m_stops, r ) );
|
|
}
|
|
}
|
|
|
|
return gradient;
|
|
}
|
|
|
|
QVariant QskGradient::interpolate(
|
|
const QskGradient& from, const QskGradient& to, qreal progress )
|
|
{
|
|
return QVariant::fromValue( from.interpolated( to, progress ) );
|
|
}
|
|
|
|
void QskGradient::clearStops()
|
|
{
|
|
if ( !m_stops.isEmpty() )
|
|
{
|
|
m_stops.clear();
|
|
m_isDirty = true;
|
|
}
|
|
}
|
|
|
|
QskHashValue QskGradient::hash( QskHashValue seed ) const
|
|
{
|
|
auto hash = qHash( m_type, seed );
|
|
hash = qHash( m_spreadMode, seed );
|
|
hash = qHash( m_stretchMode, seed );
|
|
|
|
if ( m_type != Stops )
|
|
hash = qHashBits( m_values, sizeof( m_values ), hash );
|
|
|
|
for ( const auto& stop : m_stops )
|
|
hash = stop.hash( hash );
|
|
|
|
return hash;
|
|
}
|
|
|
|
void QskGradient::setLinearDirection( Qt::Orientation orientation )
|
|
{
|
|
setLinearDirection( QskLinearDirection( orientation ) );
|
|
}
|
|
|
|
void QskGradient::setLinearDirection( qreal x1, qreal y1, qreal x2, qreal y2 )
|
|
{
|
|
setLinearDirection( QskLinearDirection( x1, y1, x2, y2 ) );
|
|
}
|
|
|
|
void QskGradient::setLinearDirection( const QskLinearDirection& direction )
|
|
{
|
|
m_type = Linear;
|
|
|
|
m_values[0] = direction.x1();
|
|
m_values[1] = direction.y1();
|
|
m_values[2] = direction.x2();
|
|
m_values[3] = direction.y2();
|
|
}
|
|
|
|
QskLinearDirection QskGradient::linearDirection() const
|
|
{
|
|
Q_ASSERT( m_type == Linear );
|
|
|
|
if ( m_type != Linear )
|
|
return QskLinearDirection( 0.0, 0.0, 0.0, 0.0 );
|
|
|
|
return QskLinearDirection( m_values[0], m_values[1], m_values[2], m_values[3] );
|
|
}
|
|
|
|
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.radiusX();
|
|
m_values[3] = direction.radiusY();
|
|
}
|
|
|
|
QskRadialDirection QskGradient::radialDirection() const
|
|
{
|
|
Q_ASSERT( m_type == Radial );
|
|
|
|
if ( m_type != Radial )
|
|
return QskRadialDirection( 0.5, 0.5, 0.0 );
|
|
|
|
return QskRadialDirection( m_values[0], m_values[1], m_values[2], m_values[3] );
|
|
}
|
|
|
|
void QskGradient::setConicDirection( qreal x, qreal y )
|
|
{
|
|
setConicDirection( QskConicDirection( x, y ) );
|
|
}
|
|
|
|
void QskGradient::setConicDirection( qreal x, qreal y,
|
|
qreal startAngle, qreal spanAngle )
|
|
{
|
|
setConicDirection( QskConicDirection( x, y, startAngle, spanAngle ) );
|
|
}
|
|
|
|
void QskGradient::setConicDirection( qreal x, qreal y,
|
|
qreal startAngle, qreal spanAngle, qreal aspectRatio )
|
|
{
|
|
const QskConicDirection dir( x, y, startAngle, spanAngle, aspectRatio );
|
|
setConicDirection( dir );
|
|
}
|
|
|
|
void QskGradient::setConicDirection( const QskConicDirection& direction )
|
|
{
|
|
m_type = Conic;
|
|
|
|
m_values[0] = direction.center().x();
|
|
m_values[1] = direction.center().y();
|
|
m_values[2] = direction.startAngle();
|
|
m_values[3] = direction.spanAngle();
|
|
m_values[4] = direction.aspectRatio();
|
|
}
|
|
|
|
QskConicDirection QskGradient::conicDirection() const
|
|
{
|
|
Q_ASSERT( m_type == Conic );
|
|
|
|
if ( m_type != Conic )
|
|
return QskConicDirection( 0.5, 0.5, 0.0, 0.0 );
|
|
|
|
QskConicDirection dir( m_values[0], m_values[1], m_values[2], m_values[3] );
|
|
dir.setAspectRatio( m_values[4] );
|
|
|
|
return dir;
|
|
}
|
|
|
|
void QskGradient::setDirection( Type type )
|
|
{
|
|
switch( type )
|
|
{
|
|
case Linear:
|
|
setLinearDirection( QskLinearDirection() );
|
|
break;
|
|
|
|
case Radial:
|
|
setRadialDirection( QskRadialDirection() );
|
|
break;
|
|
|
|
case Conic:
|
|
setConicDirection( QskConicDirection() );
|
|
break;
|
|
|
|
case Stops:
|
|
resetDirection();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void QskGradient::resetDirection()
|
|
{
|
|
m_type = Stops;
|
|
m_values[0] = m_values[1] = m_values[2] = m_values[3] = m_values[4] = 0.0;
|
|
}
|
|
|
|
QGradient QskGradient::toQGradient() const
|
|
{
|
|
QGradient g;
|
|
|
|
switch( static_cast< int >( m_type ) )
|
|
{
|
|
case Linear:
|
|
{
|
|
g = QLinearGradient( m_values[0], m_values[1], m_values[2], m_values[3] );
|
|
break;
|
|
}
|
|
|
|
case Radial:
|
|
{
|
|
g = QRadialGradient( m_values[0], m_values[1], m_values[2] );
|
|
break;
|
|
}
|
|
|
|
case Conic:
|
|
{
|
|
if ( m_values[3] != 360.0 )
|
|
{
|
|
qWarning() <<
|
|
"QskGradient: spanAngle got lost, when converting to QConicalGradient";
|
|
}
|
|
|
|
g = QConicalGradient( m_values[0], m_values[1], m_values[2] );
|
|
break;
|
|
}
|
|
}
|
|
|
|
g.setCoordinateMode( m_stretchMode == NoStretch
|
|
? QGradient::LogicalMode : QGradient::ObjectMode );
|
|
|
|
g.setSpread( static_cast< QGradient::Spread >( m_spreadMode ) );
|
|
g.setStops( qskToQGradientStops( m_stops ) );
|
|
|
|
return g;
|
|
}
|
|
|
|
#ifndef QT_NO_DEBUG_STREAM
|
|
|
|
#include <qdebug.h>
|
|
|
|
QDebug operator<<( QDebug debug, const QskGradient& gradient )
|
|
{
|
|
QDebugStateSaver saver( debug );
|
|
debug.nospace();
|
|
|
|
debug << "QskGradient(";
|
|
|
|
switch ( gradient.type() )
|
|
{
|
|
case QskGradient::Linear:
|
|
{
|
|
debug << " L(";
|
|
|
|
const auto dir = gradient.linearDirection();
|
|
debug << dir.start().x() << "," << dir.start().y()
|
|
<< "," << dir.stop().x() << "," << dir.stop().y() << ")";
|
|
|
|
break;
|
|
}
|
|
|
|
case QskGradient::Radial:
|
|
{
|
|
debug << " R(";
|
|
|
|
const auto dir = gradient.radialDirection();
|
|
|
|
debug << dir.center().x() << "," << dir.center().y()
|
|
<< "," << dir.radiusX() << dir.radiusY() << ")";
|
|
|
|
break;
|
|
}
|
|
|
|
case QskGradient::Conic:
|
|
{
|
|
debug << " C(";
|
|
|
|
const auto dir = gradient.conicDirection();
|
|
|
|
debug << dir.center().x() << "," << dir.center().y()
|
|
<< ",AR:" << dir.aspectRatio()
|
|
<< ",[" << dir.startAngle() << "," << dir.spanAngle() << "])";
|
|
break;
|
|
}
|
|
|
|
case QskGradient::Stops:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !gradient.stops().isEmpty() )
|
|
{
|
|
if ( gradient.isMonochrome() )
|
|
{
|
|
debug << ' ';
|
|
QskRgb::debugColor( debug, gradient.rgbStart() );
|
|
}
|
|
else
|
|
{
|
|
debug << " ( ";
|
|
|
|
const auto& stops = gradient.stops();
|
|
for ( int i = 0; i < stops.count(); i++ )
|
|
{
|
|
if ( i != 0 )
|
|
debug << ", ";
|
|
|
|
debug << stops[i];
|
|
}
|
|
|
|
debug << " )";
|
|
}
|
|
}
|
|
|
|
if ( gradient.stretchMode() == QskGradient::StretchToSize )
|
|
{
|
|
debug << " SS";
|
|
}
|
|
|
|
switch( gradient.spreadMode() )
|
|
{
|
|
case QskGradient::RepeatSpread:
|
|
debug << " RP";
|
|
break;
|
|
|
|
case QskGradient::ReflectSpread:
|
|
debug << " RF";
|
|
break;
|
|
|
|
case QskGradient::PadSpread:
|
|
break;
|
|
}
|
|
|
|
debug << " )";
|
|
|
|
return debug;
|
|
}
|
|
|
|
#endif
|
|
|
|
#include "moc_QskGradient.cpp"
|