From e67b0b385a0458cabf7dd7bb4dba6ebbd1689bae Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Mon, 24 Oct 2022 17:29:39 +0200 Subject: [PATCH] allowing gradient stops that do not start/end with 0.0/1.0 ( like QGradient ) --- src/common/QskGradient.cpp | 19 +++++++-- src/common/QskGradient.h | 2 +- src/nodes/QskBoxNode.cpp | 4 +- src/nodes/QskBoxRendererColorMap.h | 46 +++++++++++++++------- src/nodes/QskBoxRendererEllipse.cpp | 24 ++++++++++-- src/nodes/QskBoxRendererRect.cpp | 60 ++++++++++++++++++++++++++--- 6 files changed, 124 insertions(+), 31 deletions(-) diff --git a/src/common/QskGradient.cpp b/src/common/QskGradient.cpp index 961d03a8..496d22d4 100644 --- a/src/common/QskGradient.cpp +++ b/src/common/QskGradient.cpp @@ -31,10 +31,10 @@ static inline QskGradient::Orientation qskOrientation( Qt::Orientation o ) static inline bool qskIsGradientValid( const QskGradientStops& stops ) { - if ( stops.size() < 2 ) + if ( stops.isEmpty() ) return false; - if ( stops.first().position() != 0.0 || stops.last().position() != 1.0 ) + if ( stops.first().position() < 0.0 || stops.last().position() > 1.0 ) return false; if ( !stops.first().color().isValid() ) @@ -200,9 +200,20 @@ void QskGradient::setStops( const QskGradientStops& stops ) m_isDirty = true; } -int QskGradient::stopCount() const noexcept +int QskGradient::stepCount() const noexcept { - return m_stops.count(); + if ( !isValid() ) + return 0; + + int steps = 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 diff --git a/src/common/QskGradient.h b/src/common/QskGradient.h index de29b937..3c996b52 100644 --- a/src/common/QskGradient.h +++ b/src/common/QskGradient.h @@ -100,7 +100,7 @@ class QSK_EXPORT QskGradient Q_INVOKABLE qreal stopAt( int index ) const noexcept; Q_INVOKABLE QColor colorAt( int index ) const noexcept; - Q_INVOKABLE int stopCount() const noexcept; + int stepCount() const noexcept; private: void updateStatusBits() const; diff --git a/src/nodes/QskBoxNode.cpp b/src/nodes/QskBoxNode.cpp index 33dc0ca8..7fd09c30 100644 --- a/src/nodes/QskBoxNode.cpp +++ b/src/nodes/QskBoxNode.cpp @@ -88,7 +88,6 @@ void QskBoxNode::setBoxData( const QRectF& rect, } #endif -#if 1 const auto metricsHash = qskMetricsHash( shape, borderMetrics ); const auto colorsHash = qskColorsHash( borderColors, fillGradient ); @@ -104,7 +103,6 @@ void QskBoxNode::setBoxData( const QRectF& rect, markDirty( QSGNode::DirtyMaterial ); markDirty( QSGNode::DirtyGeometry ); -#endif if ( rect.isEmpty() ) { @@ -112,7 +110,7 @@ void QskBoxNode::setBoxData( const QRectF& rect, return; } - bool hasFill = fillGradient.isValid(); + bool hasFill = fillGradient.isVisible(); bool hasBorder = !borderMetrics.isNull(); if ( hasBorder ) diff --git a/src/nodes/QskBoxRendererColorMap.h b/src/nodes/QskBoxRendererColorMap.h index b6749b6b..b0f994df 100644 --- a/src/nodes/QskBoxRendererColorMap.h +++ b/src/nodes/QskBoxRendererColorMap.h @@ -153,16 +153,25 @@ namespace QskVertex , m_value2( value2 ) , m_stops( stops ) { - Q_ASSERT( stops.size() > 2 ); + if ( stops.first().position() > 0.0 ) + { + m_color1 = m_color2 = stops[ 0 ].color(); + m_index = 0; + } + else + { + m_color1 = stops[ 0 ].color(); + m_color2 = stops[ 1 ].color(); + m_index = 1; + } - m_color1 = stops[ 0 ].color(); - m_color2 = stops[ 1 ].color(); + m_finalIndex = m_stops.count() - 1; + if ( stops.last().position() < 1.0 ) + m_finalIndex++; m_valueStep1 = value1; - m_valueStep2 = valueAt( stops[ 1 ].position() ); + m_valueStep2 = valueAt( stops[ m_index ].position() ); m_stepSize = m_valueStep2 - m_valueStep1; - - m_index = 1; } inline qreal value() const @@ -183,13 +192,24 @@ namespace QskVertex inline bool advance() { - const auto& stop = m_stops[ ++m_index ]; + m_index++; m_color1 = m_color2; - m_color2 = stop.color(); - m_valueStep1 = m_valueStep2; - m_valueStep2 = valueAt( stop.position() ); + + if ( m_index >= m_stops.count() ) + { + m_color2 = m_color1; + m_valueStep2 = valueAt( 1.0 ); + } + else + { + const auto& stop = m_stops[ m_index ]; + + m_color2 = stop.color(); + m_valueStep2 = valueAt( stop.position() ); + } + m_stepSize = m_valueStep2 - m_valueStep1; return !isDone(); @@ -197,7 +217,7 @@ namespace QskVertex inline bool isDone() const { - return m_index >= m_stops.count() - 1; + return m_index >= m_finalIndex; } private: @@ -209,7 +229,7 @@ namespace QskVertex const qreal m_value1, m_value2; const QskGradientStops m_stops; - int m_index; + int m_index, m_finalIndex; qreal m_valueStep1, m_valueStep2, m_stepSize; Color m_color1, m_color2; }; @@ -237,7 +257,7 @@ namespace QskVertex ColoredLine* fillOrdered( ContourIterator& contourIt, qreal value1, qreal value2, const QskGradient& gradient, ColoredLine* line ) { - if ( gradient.stops().size() == 2 ) + if ( gradient.stepCount() == 1 ) { if ( value2 == 1.0 && value1 == 0.0 ) { diff --git a/src/nodes/QskBoxRendererEllipse.cpp b/src/nodes/QskBoxRendererEllipse.cpp index dfaa6caa..e164b6f6 100644 --- a/src/nodes/QskBoxRendererEllipse.cpp +++ b/src/nodes/QskBoxRendererEllipse.cpp @@ -100,7 +100,7 @@ namespace int additionalGradientStops( const QskGradient& gradient ) { - return qMax( 0, gradient.stops().count() - 2 ); + return qMax( 0, gradient.stepCount() - 1 ); } } @@ -575,10 +575,26 @@ namespace auto line = lines + additionalGradientStops( gradient ); + { + const auto &stop = stops.first(); + + if ( stop.position() > 0.0 ) + setAdditionalLine( x11, y11, x12, y12, dx1, dy1, dx2, dy2, stop, line-- ); + } + for( int i = 1; i < stops.count() - 1; i++ ) { setAdditionalLine( x11, y11, x12, y12, dx1, dy1, dx2, dy2, stops[i], line-- ); } + + { + const auto &stop = stops.last(); + + if ( stop.position() < 1.0 ) + { + setAdditionalLine( x11, y11, x12, y12, dx1, dy1, dx2, dy2, stop, line-- ); + } + } } template< class BorderMap, class FillMap > @@ -931,7 +947,7 @@ static inline int qskFillLineCount( // adding vertexes for the stops - beside the first/last if ( !gradient.isMonochrome() ) - lineCount += gradient.stops().size() - 2; + lineCount += gradient.stepCount() - 1; return lineCount; } @@ -1339,7 +1355,7 @@ void QskBoxRenderer::renderRectellipse( const QRectF& rect, { // degenerated to a rectangle - fillLineCount = gradient.stops().count(); + fillLineCount = gradient.stepCount() + 1; #if 1 // code copied from QskBoxRendererRect.cpp TODO ... @@ -1411,7 +1427,7 @@ void QskBoxRenderer::renderRectellipse( const QRectF& rect, } else if ( !gradient.isMonochrome() ) { - if ( gradient.stops().count() > 2 || + if ( gradient.stepCount() > 1 || gradient.orientation() == QskGradient::Diagonal ) { fillRandom = false; diff --git a/src/nodes/QskBoxRendererRect.cpp b/src/nodes/QskBoxRendererRect.cpp index 25d9e469..3d06fa4c 100644 --- a/src/nodes/QskBoxRendererRect.cpp +++ b/src/nodes/QskBoxRendererRect.cpp @@ -430,6 +430,12 @@ static inline void qskCreateBorder( { const auto stops = colors.bottom().stops(); + if ( stops.first().position() > 0.0 ) + { + ( line++ )->setLine( in.right, in.bottom, + out.right, out.bottom, stops.first().color() ); + } + for( const auto& stop : stops ) { const qreal x1 = in.right - stop.position() * dx1; @@ -437,11 +443,23 @@ static inline void qskCreateBorder( ( line++ )->setLine( x1, in.bottom, x2, out.bottom, stop.color() ); } + + if ( stops.last().position() < 1.0 ) + { + ( line++ )->setLine( in.left, in.bottom, + out.left, out.bottom, stops.last().color() ); + } } { const auto stops = colors.left().stops(); + if ( stops.first().position() > 0.0 ) + { + ( line++ )->setLine( in.left, in.bottom, + out.left, out.bottom, stops.first().color() ); + } + for( const auto& stop : stops ) { const qreal y1 = in.bottom + stop.position() * dy1; @@ -449,11 +467,23 @@ static inline void qskCreateBorder( ( line++ )->setLine( in.left, y1, out.left, y2, stop.color() ); } + + if ( stops.last().position() < 1.0 ) + { + ( line++ )->setLine( in.left, in.top, + out.left, out.top, stops.last().color() ); + } } { const auto stops = colors.top().stops(); + if ( stops.first().position() > 0.0 ) + { + ( line++ )->setLine( in.left, in.top, + out.left, out.top, stops.first().color() ); + } + for( const auto& stop : stops ) { const qreal x1 = in.left + stop.position() * dx1; @@ -461,11 +491,23 @@ static inline void qskCreateBorder( ( line++ )->setLine( x1, in.top, x2, out.top, stop.color() ); } + + if ( stops.last().position() < 1.0 ) + { + ( line++ )->setLine( in.right, in.top, + out.right, out.top, stops.last().color() ); + } } { const auto stops = colors.right().stops(); + if ( stops.first().position() > 0.0 ) + { + ( line++ )->setLine( in.right, in.top, + out.right, out.top, stops.first().color() ); + } + for( const auto& stop : stops ) { const qreal y1 = in.bottom + ( 1 - stop.position() ) * dy1; @@ -473,6 +515,12 @@ static inline void qskCreateBorder( ( line++ )->setLine( in.right, y1, out.right, y2, stop.color() ); } + + if ( stops.last().position() < 1.0 ) + { + ( line++ )->setLine( in.right, in.bottom, + out.right, out.bottom, stops.last().color() ); + } } } @@ -528,7 +576,7 @@ void QskBoxRenderer::renderRect( int fillLineCount = 0; if ( gradient.isVisible() && !in.isEmpty() ) { - fillLineCount = gradient.stops().count(); + fillLineCount = gradient.stepCount() + 1; if ( gradient.orientation() == QskGradient::Diagonal ) { @@ -566,10 +614,10 @@ void QskBoxRenderer::renderRect( // ### As an optimization we could check orientation and colors // to test whether colors are the same const int additionalLines = -1 - + bc.left().stops().count() - 1 - + bc.top().stops().count() - 1 - + bc.right().stops().count() - 1 - + bc.bottom().stops().count() - 1; + + bc.left().stepCount() + + bc.top().stepCount() + + bc.right().stepCount() + + bc.bottom().stepCount(); borderLineCount += qMax( additionalLines, 0 ); } @@ -589,7 +637,7 @@ void QskBoxRenderer::renderRect( } else { - bool fillRandom = gd.stops().count() <= 2; + bool fillRandom = gd.stepCount() <= 1; if ( fillRandom ) { /*