From 21e1206b2d2a58dd87de5f27c06e1d9ad15dae68 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Thu, 5 Jan 2023 13:06:32 +0100 Subject: [PATCH] QskBoxRenderer improvements --- src/nodes/QskBoxRendererColorMap.h | 51 +++++++++-- src/nodes/QskBoxRendererDEllipse.cpp | 2 +- src/nodes/QskRectRenderer.cpp | 45 +++------- src/nodes/QskRectRenderer.h | 2 +- src/nodes/QskRoundedRectRenderer.cpp | 122 ++++++--------------------- src/nodes/QskVertex.h | 76 +++++++++++++++++ 6 files changed, 160 insertions(+), 138 deletions(-) diff --git a/src/nodes/QskBoxRendererColorMap.h b/src/nodes/QskBoxRendererColorMap.h index 137b51b9..3a731b99 100644 --- a/src/nodes/QskBoxRendererColorMap.h +++ b/src/nodes/QskBoxRendererColorMap.h @@ -205,44 +205,77 @@ namespace QskVertex Color m_color1, m_color2; }; + inline ColoredLine* fillUp( ColoredLine* lines, const ColoredLine& l, int count ) + { + for ( int i = 0; i < count; i++ ) + *lines++ = l; + + return lines; + } + template< class ContourIterator, class ColorIterator > ColoredLine* fillOrdered( ContourIterator& contourIt, - ColorIterator& colorIt, ColoredLine* line ) + ColorIterator& colorIt, int lineCount, ColoredLine* lines ) { + /* + When the the vector exceeds [ 0.0, 1.0 ] we might have + gradient lines lying outside the contour. + This effect could be precalculated - however we might end + up difficult code with potential bugs. + + So we allow the allocation code to ignore the effect by + adding duplicates of the last line. + */ + const auto value0 = contourIt.value(); + ColoredLine* l = lines; + do { while ( !colorIt.isDone() && ( colorIt.value() < contourIt.value() ) ) { - if ( contourIt.setGradientLine( colorIt.value(), colorIt.color(), line ) ) - line++; + const auto value = colorIt.value(); + + /* + When having a gradient vector below 0.0 we + will have gradient lines outside of the contour + */ + + if ( value > value0 ) + contourIt.setGradientLine( value, colorIt.color(), l++ ); colorIt.advance(); } const auto color = colorIt.colorAt( contourIt.value() ); - contourIt.setContourLine( color, line++ ); + contourIt.setContourLine( color, l++ ); } while ( contourIt.advance() ); - return line; + if ( lineCount >= 0 ) + { + if ( const auto count = lineCount - ( l - lines ) ) + l = QskVertex::fillUp( l, *( l - 1 ), count ); + } + + return l; } template< class ContourIterator > ColoredLine* fillOrdered( ContourIterator& contourIt, - const QskGradient& gradient, ColoredLine* line ) + const QskGradient& gradient, int lineCount, ColoredLine* lines ) { if ( gradient.stepCount() == 1 ) { SimpleColorIterator colorIt( gradient.rgbStart(), gradient.rgbEnd() ); - line = fillOrdered( contourIt, colorIt, line ); + lines = fillOrdered( contourIt, colorIt, lineCount, lines ); } else { GradientColorIterator colorIt( gradient.stops() ); - line = fillOrdered( contourIt, colorIt, line ); + lines = fillOrdered( contourIt, colorIt, lineCount, lines ); } - return line; + return lines; } } diff --git a/src/nodes/QskBoxRendererDEllipse.cpp b/src/nodes/QskBoxRendererDEllipse.cpp index 92834776..f762f06b 100644 --- a/src/nodes/QskBoxRendererDEllipse.cpp +++ b/src/nodes/QskBoxRendererDEllipse.cpp @@ -610,7 +610,7 @@ void QskRoundedRectRenderer::renderDiagonalFill( const QskRoundedRectRenderer::M const ValueCurve curve( metrics ); DRectellipseIterator it( metrics, curve ); - auto line = QskVertex::fillOrdered( it, gradient, lines ); + auto line = QskVertex::fillOrdered( it, gradient, -1, lines ); /* There are a couple of reasons, why less points have been rendered diff --git a/src/nodes/QskRectRenderer.cpp b/src/nodes/QskRectRenderer.cpp index dc8bd76f..246b1e41 100644 --- a/src/nodes/QskRectRenderer.cpp +++ b/src/nodes/QskRectRenderer.cpp @@ -132,9 +132,6 @@ namespace inline bool setGradientLine( qreal value, Color color, ColoredLine* line ) { - if ( value <= m_corners[0].value || value >= m_corners[3].value ) - return false; - const qreal m = m_v.dy / m_v.dx; const qreal x = m_v.x + m_v.dx * value; @@ -253,19 +250,19 @@ namespace } static ColoredLine* qskAddFillLines( const Quad& rect, - const QskGradient& gradient, ColoredLine* line ) + const QskGradient& gradient, int lineCount, ColoredLine* line ) { const auto dir = gradient.linearDirection(); if ( dir.isTilted() ) { DRectIterator it( rect, dir.vector() ); - line = QskVertex::fillOrdered( it, gradient, line ); + line = QskVertex::fillOrdered( it, gradient, lineCount, line ); } else { HVRectIterator it( rect, dir.vector() ); - line = QskVertex::fillOrdered( it, gradient, line ); + line = QskVertex::fillOrdered( it, gradient, lineCount, line ); } return line; @@ -410,9 +407,9 @@ void QskRectRenderer::renderBorder( const QRectF& rect, } void QskRectRenderer::renderFill0( const QskVertex::Quad& rect, - const QskGradient& gradient, QskVertex::ColoredLine* line ) + const QskGradient& gradient, int lineCount, QskVertex::ColoredLine* line ) { - qskAddFillLines( rect, gradient, line ); + qskAddFillLines( rect, gradient, lineCount, line ); } void QskRectRenderer::renderFill( const QRectF& rect, @@ -477,10 +474,10 @@ void QskRectRenderer::renderRect( const QRectF& rect, } } - const auto line = allocateLines< ColoredLine >( + const auto lines = allocateLines< ColoredLine >( geometry, borderLineCount + fillLineCount ); - auto l = line; + auto l = lines; if ( fillLineCount > 0 ) { @@ -493,12 +490,14 @@ void QskRectRenderer::renderRect( const QRectF& rect, } else { - if ( gradient.stepCount() <= 1 && !gradient.linearDirection().isTilted() ) + const auto dir = gradient.linearDirection(); + + if ( gradient.stepCount() <= 1 && !dir.isTilted() ) { const auto c1 = gradient.rgbStart(); const auto c2 = gradient.rgbEnd(); - if ( gradient.linearDirection().isVertical() ) + if ( dir.isVertical() ) { ( l++ )->setHLine( rect.left(), rect.right(), rect.top(), c1 ); ( l++ )->setHLine( rect.left(), rect.right(), rect.bottom(), c2 ); @@ -511,27 +510,11 @@ void QskRectRenderer::renderRect( const QRectF& rect, } else { - l = qskAddFillLines( in, gradient, l ); + l = qskAddFillLines( in, gradient, fillLineCount, l ); } } - if ( l < line + fillLineCount ) - { - /* - When the the vector exceeds [ 0.0, 1.0 ] we might have - gradient lines lying outside the rectangle. - Precalculating this effect would save some memory - however - these corner cases are not worth to make the implementation - even more complicated. - So let's fill the memory with duplicates of the final - contour line instead. - */ - const auto& llast = *( l - 1 ); - while ( l < line + fillLineCount ) - *l++ = llast; - } - - Q_ASSERT( l - line == fillLineCount ); + Q_ASSERT( l - lines == fillLineCount ); } if ( borderLineCount > 0 ) @@ -546,6 +529,6 @@ void QskRectRenderer::renderRect( const QRectF& rect, l = qskAddBorderLines( rect, in, borderColors, l ); } - Q_ASSERT( l - line == borderLineCount + fillLineCount ); + Q_ASSERT( l - lines == borderLineCount + fillLineCount ); } } diff --git a/src/nodes/QskRectRenderer.h b/src/nodes/QskRectRenderer.h index c2925866..248f6b56 100644 --- a/src/nodes/QskRectRenderer.h +++ b/src/nodes/QskRectRenderer.h @@ -39,7 +39,7 @@ namespace QskVertex namespace QskRectRenderer { void renderFill0( const QskVertex::Quad&, - const QskGradient&, QskVertex::ColoredLine* ); + const QskGradient&, int lineCount, QskVertex::ColoredLine* ); } #endif diff --git a/src/nodes/QskRoundedRectRenderer.cpp b/src/nodes/QskRoundedRectRenderer.cpp index 07c3b279..a1b5d4b7 100644 --- a/src/nodes/QskRoundedRectRenderer.cpp +++ b/src/nodes/QskRoundedRectRenderer.cpp @@ -27,78 +27,6 @@ namespace BottomRight = Qt::BottomRightCorner }; - class ArcIterator - { - public: - inline ArcIterator() = default; - - inline ArcIterator( int stepCount, bool inverted = false ) - { - reset( stepCount, inverted ); - } - - void reset( int stepCount, bool inverted ) - { - m_inverted = inverted; - - if ( inverted ) - { - m_cos = 1.0; - m_sin = 0.0; - } - else - { - m_cos = 0.0; - m_sin = 1.0; - } - - m_stepIndex = 0; - m_stepCount = stepCount; - - const double angleStep = M_PI_2 / stepCount; - m_cosStep = qFastCos( angleStep ); - m_sinStep = qFastSin( angleStep ); - } - - inline bool isInverted() const { return m_inverted; } - - inline double cos() const { return m_cos; } - inline double sin() const { return m_inverted ? -m_sin : m_sin; } - - inline int step() const { return m_stepIndex; } - inline int stepCount() const { return m_stepCount; } - inline bool isDone() const { return m_stepIndex > m_stepCount; } - - inline void increment() - { - const double cos0 = m_cos; - - m_cos = m_cos * m_cosStep + m_sin * m_sinStep; - m_sin = m_sin * m_cosStep - cos0 * m_sinStep; - - ++m_stepIndex; - } - - inline void operator++() { increment(); } - - static int segmentHint( double radius ) - { - const double arcLength = radius * M_PI_2; - return qBound( 3, qCeil( arcLength / 3.0 ), 18 ); // every 3 pixels - } - - private: - double m_cos; - double m_sin; - - int m_stepIndex; - double m_cosStep; - double m_sinStep; - - int m_stepCount; - bool m_inverted; - }; - int additionalGradientStops( const QskGradient& gradient ) { return qMax( 0, gradient.stepCount() - 1 ); @@ -205,7 +133,8 @@ namespace { return m_uniform ? m_outer[ 0 ].dy : m_outer[ pos ].dy; } private: - bool m_uniform; + const bool m_uniform; + class Values { public: @@ -882,8 +811,6 @@ static inline Qt::Orientation qskQtOrientation( const QskGradient& gradient ) static inline int qskFillLineCount( const QskRoundedRectRenderer::Metrics& metrics, const QskGradient& gradient ) { - const int stepCount = metrics.corner[ 0 ].stepCount; - if ( !gradient.isVisible() ) return 0; @@ -915,7 +842,7 @@ static inline int qskFillLineCount( } else { - lineCount += 2 * ( stepCount + 1 ); + lineCount += 2 * ( metrics.corner[ 0 ].stepCount + 1 ); if ( metrics.centerQuad.left >= metrics.centerQuad.right ) lineCount--; @@ -1111,7 +1038,7 @@ static inline void qskRenderBoxRandom( static inline void qskRenderFillOrdered( const QskRoundedRectRenderer::Metrics& metrics, - const QskGradient& gradient, ColoredLine* lines ) + const QskGradient& gradient, int lineCount, ColoredLine* lines ) { /* The algo for irregular radii at opposite corners is not yet @@ -1121,12 +1048,12 @@ static inline void qskRenderFillOrdered( if ( gradient.linearDirection().isHorizontal() ) { HRectEllipseIterator it( metrics ); - QskVertex::fillOrdered( it, gradient, lines ); + QskVertex::fillOrdered( it, gradient, lineCount, lines ); } else { VRectEllipseIterator it( metrics ); - QskVertex::fillOrdered( it, gradient, lines ); + QskVertex::fillOrdered( it, gradient, lineCount, lines ); } } @@ -1434,6 +1361,7 @@ void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect, QSGGeometry& geometry ) { const Metrics metrics( rect, shape, border ); + const auto isTilted = gradient.linearDirection().isTilted(); int fillLineCount = 0; @@ -1446,7 +1374,7 @@ void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect, fillLineCount = gradient.stepCount() + 1; #if 1 - if ( gradient.linearDirection().isTilted() ) + if ( isTilted ) { if ( metrics.centerQuad.width == metrics.centerQuad.height ) { @@ -1489,7 +1417,7 @@ void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect, bool extraLine = false; if ( borderLineCount > 0 && fillLineCount > 0 ) { - if ( !gradient.isMonochrome() && gradient.linearDirection().isTilted() ) + if ( !gradient.isMonochrome() && isTilted ) { /* The filling ends at 45° and we have no implementation @@ -1501,7 +1429,7 @@ void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect, } } - auto line = allocateLines< ColoredLine >( geometry, lineCount ); + auto lines = allocateLines< ColoredLine >( geometry, lineCount ); bool fillRandom = true; if ( fillLineCount > 0 ) @@ -1512,7 +1440,7 @@ void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect, } else if ( !gradient.isMonochrome() ) { - if ( gradient.stepCount() > 1 || gradient.linearDirection().isTilted() ) + if ( gradient.stepCount() > 1 || isTilted ) fillRandom = false; } @@ -1534,24 +1462,25 @@ void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect, if ( fillRandom ) { qskRenderBoxRandom( metrics, borderColors, - gradient, line, line + fillLineCount ); + gradient, lines, lines + fillLineCount ); } else { if ( metrics.isTotallyCropped ) { - QskRectRenderer::renderFill0( metrics.innerQuad, gradient, line ); + QskRectRenderer::renderFill0( metrics.innerQuad, + gradient, fillLineCount, lines ); } - else if ( gradient.linearDirection().isTilted() ) + else if ( isTilted ) { - renderDiagonalFill( metrics, gradient, fillLineCount, line ); + renderDiagonalFill( metrics, gradient, fillLineCount, lines ); } else { - qskRenderFillOrdered( metrics, gradient, line ); + qskRenderFillOrdered( metrics, gradient, fillLineCount, lines ); } - auto borderLines = line + fillLineCount; + auto borderLines = lines + fillLineCount; if ( extraLine ) borderLines++; @@ -1560,7 +1489,7 @@ void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect, if ( extraLine ) { - const auto l = line + fillLineCount; + const auto l = lines + fillLineCount; l[ 0 ].p1 = l[ -1 ].p2; l[ 0 ].p2 = l[ 1 ].p1; } @@ -1570,21 +1499,22 @@ void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect, { if ( fillRandom ) { - qskRenderFillRandom( metrics, gradient, line ); + qskRenderFillRandom( metrics, gradient, lines ); } else { if ( metrics.isTotallyCropped ) { - QskRectRenderer::renderFill0( metrics.innerQuad, gradient, line ); + QskRectRenderer::renderFill0( metrics.innerQuad, + gradient, fillLineCount, lines ); } - else if ( gradient.linearDirection().isTilted() ) + else if ( isTilted ) { - renderDiagonalFill( metrics, gradient, fillLineCount, line ); + renderDiagonalFill( metrics, gradient, fillLineCount, lines ); } else { - qskRenderFillOrdered( metrics, gradient, line ); + qskRenderFillOrdered( metrics, gradient, fillLineCount, lines ); } } } @@ -1596,6 +1526,6 @@ void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect, border colors, we could treat it like filling without border. TODO ... */ #endif - qskRenderBorder( metrics, Qt::Vertical, borderColors, line ); + qskRenderBorder( metrics, Qt::Vertical, borderColors, lines ); } } diff --git a/src/nodes/QskVertex.h b/src/nodes/QskVertex.h index 6a6b7d09..91642ed0 100644 --- a/src/nodes/QskVertex.h +++ b/src/nodes/QskVertex.h @@ -10,6 +10,7 @@ #include #include +#include namespace QskVertex { @@ -213,6 +214,81 @@ namespace QskVertex }; } +namespace +{ + class ArcIterator + { + public: + inline ArcIterator() = default; + + inline ArcIterator( int stepCount, bool inverted = false ) + { + reset( stepCount, inverted ); + } + + void reset( int stepCount, bool inverted ) + { + m_inverted = inverted; + + if ( inverted ) + { + m_cos = 1.0; + m_sin = 0.0; + } + else + { + m_cos = 0.0; + m_sin = 1.0; + } + + m_stepIndex = 0; + m_stepCount = stepCount; + + const double angleStep = M_PI_2 / stepCount; + m_cosStep = qFastCos( angleStep ); + m_sinStep = qFastSin( angleStep ); + } + + inline bool isInverted() const { return m_inverted; } + + inline double cos() const { return m_cos; } + inline double sin() const { return m_inverted ? -m_sin : m_sin; } + + inline int step() const { return m_stepIndex; } + inline int stepCount() const { return m_stepCount; } + inline bool isDone() const { return m_stepIndex > m_stepCount; } + + inline void increment() + { + const double cos0 = m_cos; + + m_cos = m_cos * m_cosStep + m_sin * m_sinStep; + m_sin = m_sin * m_cosStep - cos0 * m_sinStep; + + ++m_stepIndex; + } + + inline void operator++() { increment(); } + + static int segmentHint( double radius ) + { + const double arcLength = radius * M_PI_2; + return qBound( 3, qCeil( arcLength / 3.0 ), 18 ); // every 3 pixels + } + + private: + double m_cos; + double m_sin; + + int m_stepIndex; + double m_cosStep; + double m_sinStep; + + int m_stepCount; + bool m_inverted; + }; +} + namespace QskVertex { void debugGeometry( const QSGGeometry& );