diff --git a/src/nodes/QskArcNode.cpp b/src/nodes/QskArcNode.cpp index 34a91c52..43782d95 100644 --- a/src/nodes/QskArcNode.cpp +++ b/src/nodes/QskArcNode.cpp @@ -11,21 +11,11 @@ #include "QskMargins.h" #include "QskGradient.h" #include "QskShapeNode.h" -#include "QskStrokeNode.h" #include "QskSGNode.h" #include "QskShadowMetrics.h" #include -#define ARC_BORDER_NODE - -#ifdef ARC_BORDER_NODE - using BorderNode = QskArcRenderNode; -#else - #include - using BorderNode = QskStrokeNode; -#endif - namespace { enum NodeRole @@ -33,9 +23,7 @@ namespace ShadowRole, PathRole, - ArcRole, - - BorderRole + ArcRole }; } @@ -56,13 +44,18 @@ static inline QskGradient qskEffectiveGradient( return gradient; } -static void qskUpdateChildren( QSGNode* parentNode, quint8 role, QSGNode* node ) +template< typename Node > +inline Node* qskInsertOrRemoveNode( QSGNode* parentNode, quint8 role, bool isValid ) { - static const QVector< quint8 > roles = - { ShadowRole, PathRole, ArcRole, BorderRole }; + using namespace QskSGNode; - auto oldNode = QskSGNode::findChildNode( parentNode, role ); - QskSGNode::replaceChildNode( roles, role, parentNode, oldNode, node ); + Node* oldNode = static_cast< Node* >( findChildNode( parentNode, role ) ); + Node* newNode = isValid ? ensureNode< Node >( oldNode ) : nullptr; + + static const QVector< quint8 > roles = { ShadowRole, PathRole, ArcRole }; + replaceChildNode( roles, role, parentNode, oldNode, newNode ); + + return newNode; } QskArcNode::QskArcNode() @@ -93,40 +86,25 @@ void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics const auto metricsArc = arcMetrics.toAbsolute( rect.size() ); - auto shadowNode = static_cast< QskArcShadowNode* >( - QskSGNode::findChildNode( this, ShadowRole ) ); - - auto pathNode = static_cast< QskShapeNode* >( - QskSGNode::findChildNode( this, PathRole ) ); - - auto arcNode = static_cast< QskArcRenderNode* >( - QskSGNode::findChildNode( this, ArcRole ) ); - - auto borderNode = static_cast< BorderNode* >( - QskSGNode::findChildNode( this, BorderRole ) ); - if ( metricsArc.isNull() || rect.isEmpty() ) { - delete shadowNode; - delete pathNode; - delete arcNode; - delete borderNode; + delete QskSGNode::findChildNode( this, ShadowRole ); + delete QskSGNode::findChildNode( this, PathRole ); + delete QskSGNode::findChildNode( this, ArcRole ); + return; } - const auto isFillNodeVisible = gradient.isVisible(); - const auto isBorderNodeVisible = ( borderWidth > 0.0 ) && ( borderColor.alpha() > 0 ); - const auto isShadowNodeVisible = isFillNodeVisible && - shadowColor.isValid() && ( shadowColor.alpha() > 0.0 ); + const auto hasFilling = gradient.isVisible(); + const auto hasBorder = ( borderWidth > 0.0 ) + && borderColor.isValid() && ( borderColor.alpha() > 0 ); + const auto hasShadow = shadowColor.isValid() && ( shadowColor.alpha() > 0 ); - if ( isShadowNodeVisible ) + auto shadowNode = qskInsertOrRemoveNode< QskArcShadowNode >( + this, ShadowRole, hasFilling && hasShadow ); + + if ( shadowNode ) { - if ( shadowNode == nullptr ) - { - shadowNode = new QskArcShadowNode; - QskSGNode::setNodeRole( shadowNode, ShadowRole ); - } - /* The shader of the shadow node is for circular arcs and we have some unwanted scaling issues for the spread/blur values when having ellipsoid @@ -141,95 +119,23 @@ void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics shadowNode->setShadowData( shadowRect, spreadRadius, sm.blurRadius(), metricsArc.startAngle(), metricsArc.spanAngle(), shadowColor ); } - else + + auto pathNode = qskInsertOrRemoveNode< QskShapeNode >( + this, PathRole, hasFilling && !QskArcRenderer::isGradientSupported( gradient ) ); + + if ( pathNode ) { - delete shadowNode; - shadowNode = nullptr; + const auto path = metricsArc.painterPath( rect, radial ); + pathNode->updateNode( path, QTransform(), rect, + qskEffectiveGradient( gradient, metricsArc ) ); } - if ( isFillNodeVisible ) + auto arcNode = qskInsertOrRemoveNode< QskArcRenderNode >( + this, ArcRole, hasBorder || ( hasFilling && !pathNode ) ); + + if ( arcNode ) { - if ( QskArcRenderer::isGradientSupported( gradient ) ) - { - delete pathNode; - pathNode = nullptr; - - if ( arcNode == nullptr ) - { - arcNode = new QskArcRenderNode; - QskSGNode::setNodeRole( arcNode, ArcRole ); - } - - arcNode->updateNode( rect, metricsArc, radial, - borderWidth, QColor(), gradient ); - } - else - { - delete arcNode; - arcNode = nullptr; - - if ( pathNode == nullptr ) - { - pathNode = new QskShapeNode; - QskSGNode::setNodeRole( pathNode, PathRole ); - } - - const auto path = metricsArc.painterPath( rect, radial ); - pathNode->updateNode( path, QTransform(), rect, - qskEffectiveGradient( gradient, metricsArc ) ); - } + arcNode->updateNode( rect, metricsArc, radial, + borderWidth, borderColor, pathNode ? QskGradient() : gradient ); } - else - { - delete pathNode; - pathNode = nullptr; - - delete arcNode; - arcNode = nullptr; - } - - if ( isBorderNodeVisible ) - { - if ( borderNode == nullptr ) - { - borderNode = new BorderNode; - QskSGNode::setNodeRole( borderNode, BorderRole ); - } - -#ifdef ARC_BORDER_NODE - borderNode->updateNode( rect, metricsArc, radial, - borderWidth, borderColor, QskGradient() ); -#else - { - /* - Qt centers the border over the boundaries, while we - always want to have the complete borders inside. - So we have to subtract 0.5 * border. - */ - - QPen pen( borderColor, borderWidth ); - pen.setCapStyle( Qt::SquareCap ); - pen.setJoinStyle( Qt::MiterJoin ); - - const auto b2 = 0.5 * borderWidth; - const auto r = rect.adjusted( b2, b2, -b2, -b2 ); - - const auto m = QskArcMetrics( metricsArc.startAngle(), metricsArc.spanAngle(), - metricsArc.thickness() - borderWidth ); - - const auto path = m.painterPath( r, radial ); - borderNode->updateNode( path, QTransform(), pen ); - } -#endif - } - else - { - delete borderNode; - borderNode = nullptr; - } - - qskUpdateChildren( this, ShadowRole, shadowNode ); - qskUpdateChildren( this, PathRole, pathNode ); - qskUpdateChildren( this, ArcRole, arcNode ); - qskUpdateChildren( this, BorderRole, borderNode ); } diff --git a/src/nodes/QskArcRenderNode.cpp b/src/nodes/QskArcRenderNode.cpp index 85ad57b6..9a7119f7 100644 --- a/src/nodes/QskArcRenderNode.cpp +++ b/src/nodes/QskArcRenderNode.cpp @@ -70,13 +70,10 @@ void QskArcRenderNode::updateNode( const auto metrics = arcMetrics.toAbsolute( rect.size() ); const auto borderMax = 0.5 * metrics.thickness(); - const auto hasFilling = gradient.isVisible() - && ( borderWidth < borderMax ); - bool visible = !( rect.isEmpty() || metrics.isNull() ); if ( visible ) { - visible = hasFilling; + visible = gradient.isVisible() && ( borderWidth < borderMax ); if ( !visible ) { visible = ( borderWidth > 0.0 ) @@ -92,11 +89,13 @@ void QskArcRenderNode::updateNode( return; } + borderWidth = qMin( borderWidth, borderMax ); + QskHashValue hash = 3496; hash = qHashBits( &rect, sizeof( QRectF ), hash ); hash = qHash( borderWidth, hash ); - hash = qHash( borderColor.rgba(), hash ); + hash = qHashBits( &borderColor, sizeof( borderColor ), hash ); hash = metrics.hash( hash ); hash = gradient.hash( hash ); hash = qHash( radial, hash ); @@ -105,18 +104,8 @@ void QskArcRenderNode::updateNode( { d->hash = hash; - if ( borderWidth > 0.0 && borderColor.isValid() ) - { - borderWidth = std::min( borderWidth, borderMax ); - QskArcRenderer::renderBorder( - rect, metrics, radial, borderWidth, borderColor, *geometry() ); - } - - if ( hasFilling ) - { - QskArcRenderer::renderFillGeometry( - rect, metrics, radial, borderWidth, gradient, *geometry() ); - } + QskArcRenderer::renderArc( rect, metrics, radial, + borderWidth, gradient, borderColor, *geometry() ); markDirty( QSGNode::DirtyGeometry ); markDirty( QSGNode::DirtyMaterial ); diff --git a/src/nodes/QskArcRenderer.cpp b/src/nodes/QskArcRenderer.cpp index 6d473f15..495e326f 100644 --- a/src/nodes/QskArcRenderer.cpp +++ b/src/nodes/QskArcRenderer.cpp @@ -8,6 +8,7 @@ #include "QskGradient.h" #include "QskVertex.h" #include "QskBoxColorMap.h" +#include "QskRgbValue.h" #include #include @@ -210,12 +211,12 @@ namespace { public: ArcStroker( const QRectF&, const QskArcMetrics&, - bool radial, const QskGradient&, const QColor& borderColor ); + bool radial, const QskGradient&, const QskVertex::Color& ); int fillCount() const; - int setFillLines( qreal thickness, qreal border, QskVertex::ColoredLine* ) const; - int borderCount() const; + + int setFillLines( qreal thickness, qreal border, QskVertex::ColoredLine* ) const; int setBorderLines( qreal thickness, qreal border, QskVertex::ColoredLine* ) const; private: @@ -240,7 +241,7 @@ namespace }; ArcStroker::ArcStroker( const QRectF& rect, const QskArcMetrics& metrics, - bool radial, const QskGradient& gradient, const QColor& borderColor ) + bool radial, const QskGradient& gradient, const QskVertex::Color& borderColor ) : m_rect( rect ) , m_radians1( qDegreesToRadians( metrics.startAngle() ) ) , m_radians2( qDegreesToRadians( metrics.endAngle() ) ) @@ -264,6 +265,9 @@ namespace int ArcStroker::fillCount() const { + if ( !m_gradient.isVisible() ) + return 0; + return arcLineCount() + m_gradient.stepCount() - 1; } @@ -325,6 +329,9 @@ namespace int ArcStroker::borderCount() const { + if ( m_borderColor.a == 0 ) + return 0; + auto count = 2 * arcLineCount(); if ( !m_closed ) count += 2 * 3; @@ -390,62 +397,74 @@ bool QskArcRenderer::isGradientSupported( const QskGradient& gradient ) return true; } -void QskArcRenderer::renderFillGeometry( const QRectF& rect, - const QskArcMetrics& metrics, bool radial, qreal borderWidth, - const QskGradient& gradient, QSGGeometry& geometry ) +void QskArcRenderer::renderArc( const QRectF& rect, const QskArcMetrics& metrics, + bool radial, const QskGradient& gradient, QSGGeometry& geometry ) +{ + renderArc( rect, metrics, radial, 0, gradient, QColor(), geometry ); +} + +void QskArcRenderer::renderArc( const QRectF& rect, const QskArcMetrics& metrics, + bool radial, qreal borderWidth, const QskGradient& gradient, + const QColor& borderColor, QSGGeometry& geometry ) { geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip ); - ArcStroker stroker( rect, metrics, radial, gradient, QColor() ); + ArcStroker stroker( rect, metrics, radial, gradient, + borderColor.isValid() ? borderColor : QColor( 0, 0, 0, 0 ) ); - const auto lineCount = stroker.fillCount(); + const auto borderCount = stroker.borderCount(); + const auto fillCount = stroker.fillCount(); + + auto lineCount = borderCount + fillCount; + if ( borderCount && fillCount ) + lineCount++; // connecting line const auto lines = qskAllocateColoredLines( geometry, lineCount ); if ( lines == nullptr ) return; - const auto effectiveCount = stroker.setFillLines( - metrics.thickness(), borderWidth, lines ); - - if ( effectiveCount > lineCount ) + if ( fillCount ) { - qFatal( "geometry: allocated memory exceeded: %d vs. %d", - effectiveCount, lineCount ); - } - - if ( effectiveCount < lineCount ) - { - /* - Gradient or contour lines might be at the same position - and we end up with less lines, than expected. - */ - - for ( int i = effectiveCount; i < lineCount; i++ ) - lines[i] = lines[i - 1]; - } -} - -void QskArcRenderer::renderBorder( const QRectF& rect, const QskArcMetrics& metrics, - bool radial, qreal borderWidth, const QColor& borderColor, QSGGeometry& geometry ) -{ - Q_ASSERT( borderWidth > 0.0 ); - - geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip ); - - ArcStroker stroker( rect, metrics, radial, QskGradient(), borderColor ); - - const auto lineCount = stroker.borderCount(); - - const auto lines = qskAllocateColoredLines( geometry, lineCount ); - if ( lines ) - { - const auto effectiveCount = stroker.setBorderLines( + const auto effectiveCount = stroker.setFillLines( metrics.thickness(), borderWidth, lines ); - if ( lineCount != effectiveCount ) + if ( effectiveCount > fillCount ) + { + qFatal( "geometry: allocated memory exceeded: %d vs. %d", + effectiveCount, fillCount ); + } + + if ( effectiveCount < fillCount ) + { + /* + Gradient or contour lines might be at the same position + and we end up with less lines, than expected. + */ + + for ( int i = effectiveCount; i < fillCount; i++ ) + lines[i] = lines[i - 1]; + } + } + + if ( borderCount > 0 ) + { + auto borderLines = lines + lineCount - borderCount; + + const auto effectiveCount = stroker.setBorderLines( + metrics.thickness(), borderWidth, borderLines ); + + if ( borderCount != effectiveCount ) { qFatal( "geometry: allocated memory does not match: %d vs. %d", - effectiveCount, lineCount ); + effectiveCount, borderCount ); + } + + if ( fillCount && borderCount ) + { + const auto idx = fillCount; + + lines[idx].p1 = lines[idx-1].p2; + lines[idx].p2 = lines[idx+1].p1; } } } diff --git a/src/nodes/QskArcRenderer.h b/src/nodes/QskArcRenderer.h index 2a8abe02..ca38e38b 100644 --- a/src/nodes/QskArcRenderer.h +++ b/src/nodes/QskArcRenderer.h @@ -37,11 +37,11 @@ namespace QskArcRenderer */ QSK_EXPORT bool isGradientSupported( const QskGradient& ); - QSK_EXPORT void renderBorder( const QRectF&, const QskArcMetrics&, - bool radial, qreal borderWidth, const QColor& borderColor, QSGGeometry& ); + QSK_EXPORT void renderArc( const QRectF&, const QskArcMetrics&, bool radial, + qreal borderWidth, const QskGradient&, const QColor& borderColor, QSGGeometry& ); - QSK_EXPORT void renderFillGeometry( const QRectF&, const QskArcMetrics&, - bool radial, qreal borderWidth, const QskGradient&, QSGGeometry& ); + QSK_EXPORT void renderArc( const QRectF&, const QskArcMetrics&, bool radial, + const QskGradient&, QSGGeometry& ); } #endif