From 125fe1e9f463f812118c4c99d57e81051af1dd05 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Mon, 5 Dec 2022 12:34:08 +0100 Subject: [PATCH] QskBoxNode renamed to QskBoxRectangleNode, QskShadedBoxNode renamed to QskBoxNode --- examples/frames/Frame.cpp | 10 +- examples/frames/Frame.h | 4 +- examples/iotdashboard/DiagramSkinlet.cpp | 10 +- playground/dials/DialSkinlet.cpp | 1 - src/controls/QskMenuSkinlet.cpp | 1 - src/controls/QskPageIndicatorSkinlet.cpp | 1 - src/controls/QskSkinlet.cpp | 28 +- src/nodes/QskBoxNode.cpp | 339 +++++------------- src/nodes/QskBoxNode.h | 20 +- src/nodes/QskBoxRectangleNode.cpp | 287 +++++++++++++++ ...kShadedBoxNode.h => QskBoxRectangleNode.h} | 28 +- src/nodes/QskShadedBoxNode.cpp | 139 ------- src/src.pro | 4 +- 13 files changed, 435 insertions(+), 437 deletions(-) create mode 100644 src/nodes/QskBoxRectangleNode.cpp rename src/nodes/{QskShadedBoxNode.h => QskBoxRectangleNode.h} (51%) delete mode 100644 src/nodes/QskShadedBoxNode.cpp diff --git a/examples/frames/Frame.cpp b/examples/frames/Frame.cpp index f48fc643..31ccd440 100644 --- a/examples/frames/Frame.cpp +++ b/examples/frames/Frame.cpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include @@ -98,7 +98,7 @@ void Frame::updateNode( QSGNode* parentNode ) const quint8 nodeRole = 0; - auto node = static_cast< QskBoxNode* >( + auto node = static_cast< QskBoxRectangleNode* >( QskSGNode::findChildNode( parentNode, nodeRole ) ); const QRectF rect = contentsRect(); @@ -110,7 +110,7 @@ void Frame::updateNode( QSGNode* parentNode ) if ( node == nullptr ) { - node = new QskBoxNode; + node = new QskBoxRectangleNode; QskSGNode::setNodeRole( node, nodeRole ); } @@ -120,7 +120,7 @@ void Frame::updateNode( QSGNode* parentNode ) parentNode->appendChildNode( node ); } -void Frame::updateFrameNode( const QRectF& rect, QskBoxNode* node ) +void Frame::updateFrameNode( const QRectF& rect, QskBoxRectangleNode* node ) { const QColor dark = m_color.darker( 150 ); const QColor light = m_color.lighter( 150 ); @@ -150,7 +150,7 @@ void Frame::updateFrameNode( const QRectF& rect, QskBoxNode* node ) const QskBoxBorderColors borderColors( c1, c1, c2, c2 ); const qreal radius = effectiveRadius( rect, m_radius ); - node->setBoxData( rect, radius, m_frameWidth, borderColors, m_color ); + node->updateNode( rect, radius, m_frameWidth, borderColors, m_color ); } #include "moc_Frame.cpp" diff --git a/examples/frames/Frame.h b/examples/frames/Frame.h index 55c2f687..b8ed2f5f 100644 --- a/examples/frames/Frame.h +++ b/examples/frames/Frame.h @@ -7,7 +7,7 @@ #include "QskControl.h" -class QskBoxNode; +class QskBoxRectangleNode; class Frame : public QskControl { @@ -62,7 +62,7 @@ class Frame : public QskControl void updateNode( QSGNode* ) override; private: - void updateFrameNode( const QRectF&, QskBoxNode* ); + void updateFrameNode( const QRectF&, QskBoxRectangleNode* ); Style m_style; QColor m_color; diff --git a/examples/iotdashboard/DiagramSkinlet.cpp b/examples/iotdashboard/DiagramSkinlet.cpp index 790ccf38..82d5cffb 100644 --- a/examples/iotdashboard/DiagramSkinlet.cpp +++ b/examples/iotdashboard/DiagramSkinlet.cpp @@ -10,7 +10,7 @@ #include #include -#include +#include #include namespace @@ -142,15 +142,15 @@ QSGNode* DiagramSkinlet::updateChartNode( const Diagram* diagram, QSGNode* node for( int k = 0; k < dataPoints.size(); ++k ) { - QskBoxNode* barNode; + QskBoxRectangleNode* barNode; if( barsNode->childCount() > k ) { - barNode = static_cast< QskBoxNode* >( barsNode->childAtIndex( k ) ); + barNode = static_cast< QskBoxRectangleNode* >( barsNode->childAtIndex( k ) ); } else { - barNode = new QskBoxNode; + barNode = new QskBoxRectangleNode; barsNode->appendChildNode( barNode ); } @@ -172,7 +172,7 @@ QSGNode* DiagramSkinlet::updateChartNode( const Diagram* diagram, QSGNode* node color = diagram->color( barSubcontrol ); const auto shape = diagram->boxShapeHint( barSubcontrol ); - barNode->setBoxData( barRect, shape, {}, {}, color ); + barNode->updateNode( barRect, shape, {}, {}, color ); } } else diff --git a/playground/dials/DialSkinlet.cpp b/playground/dials/DialSkinlet.cpp index ffee9a37..ef40c786 100644 --- a/playground/dials/DialSkinlet.cpp +++ b/playground/dials/DialSkinlet.cpp @@ -8,7 +8,6 @@ #include #include -#include #include #include #include diff --git a/src/controls/QskMenuSkinlet.cpp b/src/controls/QskMenuSkinlet.cpp index fb068fb4..2bda28d4 100644 --- a/src/controls/QskMenuSkinlet.cpp +++ b/src/controls/QskMenuSkinlet.cpp @@ -6,7 +6,6 @@ #include "QskMenuSkinlet.h" #include "QskMenu.h" -#include "QskBoxNode.h" #include "QskGraphic.h" #include "QskColorFilter.h" #include "QskTextOptions.h" diff --git a/src/controls/QskPageIndicatorSkinlet.cpp b/src/controls/QskPageIndicatorSkinlet.cpp index 6b57eda2..c6680408 100644 --- a/src/controls/QskPageIndicatorSkinlet.cpp +++ b/src/controls/QskPageIndicatorSkinlet.cpp @@ -6,7 +6,6 @@ #include "QskPageIndicatorSkinlet.h" #include "QskPageIndicator.h" -#include "QskBoxNode.h" #include "QskSGNode.h" #include "QskFunctions.h" diff --git a/src/controls/QskSkinlet.cpp b/src/controls/QskSkinlet.cpp index 94caa691..faa8740b 100644 --- a/src/controls/QskSkinlet.cpp +++ b/src/controls/QskSkinlet.cpp @@ -10,9 +10,9 @@ #include "QskArcMetrics.h" #include "QskBoxBorderColors.h" #include "QskBoxBorderMetrics.h" -#include "QskBoxClipNode.h" #include "QskBoxNode.h" -#include "QskShadedBoxNode.h" +#include "QskBoxClipNode.h" +#include "QskBoxRectangleNode.h" #include "QskBoxShapeMetrics.h" #include "QskBoxHints.h" #include "QskColorFilter.h" @@ -175,7 +175,7 @@ static inline QskTextColors qskTextColors( return c; } -static inline QSGNode* qskUpdateShadedBoxNode( +static inline QSGNode* qskUpdateBoxNode( const QskSkinnable*, QSGNode* node, const QRectF& rect, const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics, const QskBoxBorderColors& borderColors, const QskGradient& gradient, @@ -190,14 +190,14 @@ static inline QSGNode* qskUpdateShadedBoxNode( if ( qskIsBoxVisible( absoluteMetrics, borderColors, gradient ) ) { - auto boxNode = static_cast< QskShadedBoxNode* >( node ); + auto boxNode = static_cast< QskBoxNode* >( node ); if ( boxNode == nullptr ) - boxNode = new QskShadedBoxNode(); + boxNode = new QskBoxNode(); const auto absoluteShape = shape.toAbsolute( size ); const auto absoluteShadowMetrics = shadowMetrics.toAbsolute( size ); - boxNode->setBoxData( rect, absoluteShape, absoluteMetrics, + boxNode->updateNode( rect, absoluteShape, absoluteMetrics, borderColors, gradient, absoluteShadowMetrics, shadowColor ); return boxNode; @@ -350,12 +350,12 @@ QSGNode* QskSkinlet::updateBackgroundNode( if ( !gradient.isValid() ) return nullptr; - auto boxNode = static_cast< QskBoxNode* >( node ); - if ( boxNode == nullptr ) - boxNode = new QskBoxNode(); + auto rectNode = static_cast< QskBoxRectangleNode* >( node ); + if ( rectNode == nullptr ) + rectNode = new QskBoxRectangleNode(); - boxNode->setBoxData( rect, gradient ); - return boxNode; + rectNode->updateNode( rect, gradient ); + return rectNode; } QSGNode* QskSkinlet::updateDebugNode( @@ -432,7 +432,7 @@ QSGNode* QskSkinlet::updateBoxNode( const QskSkinnable* skinnable, const auto shadowMetrics = skinnable->shadowMetricsHint( subControl ); const auto shadowColor = skinnable->shadowColorHint( subControl ); - return qskUpdateShadedBoxNode( skinnable, node, + return qskUpdateBoxNode( skinnable, node, boxRect, shape, borderMetrics, borderColors, fillGradient, shadowMetrics, shadowColor ); } @@ -442,7 +442,7 @@ QSGNode* QskSkinlet::updateBoxNode( const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics, const QskBoxBorderColors& borderColors, const QskGradient& fillGradient ) { - return qskUpdateShadedBoxNode( skinnable, node, + return qskUpdateBoxNode( skinnable, node, rect, shape, borderMetrics, borderColors, fillGradient, QskShadowMetrics(), QColor() ); } @@ -450,7 +450,7 @@ QSGNode* QskSkinlet::updateBoxNode( QSGNode* QskSkinlet::updateBoxNode( const QskSkinnable* skinnable, QSGNode* node, const QRectF& rect, const QskBoxHints& hints ) { - return qskUpdateShadedBoxNode( skinnable, node, rect, + return qskUpdateBoxNode( skinnable, node, rect, hints.shape, hints.borderMetrics, hints.borderColors, hints.gradient, hints.shadowMetrics, hints.shadowColor ); } diff --git a/src/nodes/QskBoxNode.cpp b/src/nodes/QskBoxNode.cpp index 8b4b946a..e9632821 100644 --- a/src/nodes/QskBoxNode.cpp +++ b/src/nodes/QskBoxNode.cpp @@ -4,283 +4,136 @@ *****************************************************************************/ #include "QskBoxNode.h" -#include "QskBoxBorderColors.h" -#include "QskBoxBorderMetrics.h" -#include "QskBoxRenderer.h" -#include "QskBoxShapeMetrics.h" +#include "QskBoxFillNode.h" +#include "QskBoxShadowNode.h" +#include "QskBoxRectangleNode.h" +#include "QskSGNode.h" + #include "QskGradient.h" #include "QskGradientDirection.h" +#include "QskShadowMetrics.h" +#include "QskBoxBorderMetrics.h" +#include "QskBoxBorderColors.h" -#include -#include -#include - -QSK_QT_PRIVATE_BEGIN -#include -QSK_QT_PRIVATE_END - -Q_GLOBAL_STATIC( QSGVertexColorMaterial, qskMaterialVertex ) - -static inline QskHashValue qskMetricsHash( - const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics ) +namespace { - QskHashValue hash = 13000; - - hash = shape.hash( hash ); - return borderMetrics.hash( hash ); -} - -static inline QskHashValue qskColorsHash( - const QskBoxBorderColors& borderColors, const QskGradient& fillGradient ) -{ - QskHashValue hash = 13000; - hash = borderColors.hash( hash ); - return fillGradient.hash( hash ); -} - -#if 1 - - -static inline QskGradient qskEffectiveGradient( const QskGradient& gradient ) -{ - QskGradient g; - - if ( gradient.isVisible() ) + enum Role { - if ( gradient.isMonochrome() ) - { - g.setLinearDirection( Qt::Vertical ); - g.setStops( gradient.rgbStart() ); - } - else - { + ShadowRole, + BoxRole, + FillRole + }; +} - switch( gradient.type() ) +void qskUpdateChildren( QSGNode* parentNode, quint8 role, QSGNode* node ) +{ + static const QVector< quint8 > roles = { ShadowRole, BoxRole, FillRole }; + + auto oldNode = QskSGNode::findChildNode( parentNode, role ); + QskSGNode::replaceChildNode( roles, role, parentNode, oldNode, node ); +} + +template< typename Node > +inline Node* qskNode( QSGNode* parentNode, quint8 role ) +{ + using namespace QskSGNode; + + auto node = static_cast< Node* > ( findChildNode( parentNode, role ) ); + + if ( node == nullptr ) + { + node = new Node(); + setNodeRole( node, role ); + } + + return node; +} + +static inline bool qskIsBoxGradient( const QskGradient& gradient ) +{ + if ( !gradient.isVisible() || gradient.isMonochrome() ) + return true; + + switch( gradient.type() ) + { + case QskGradient::Linear: + { + auto dir = gradient.linearDirection(); + + if ( dir.isTilted() ) { - case QskGradient::Linear: - { - auto dir = gradient.linearDirection(); - - if ( dir.isTilted() ) - { - dir.setStart( 0.0, 0.0 ); - dir.setStop( 1.0, 1.0 ); - } - - g.setLinearDirection( dir ); - - break; - } - case QskGradient::Radial: - case QskGradient::Conic: - { - qWarning() << "QskBoxNode does not support radial/conic gradients"; - g.setLinearDirection( Qt::Vertical ); - - break; - } - case QskGradient::Stops: - { - g.setLinearDirection( Qt::Vertical ); - break; - } + // only diagonal from topLeft to bottomRight + return ( dir.x1() == dir.x2() ) && ( dir.y1() == dir.y2() ); } - g.setStops( gradient.stops() ); + return true; + } + case QskGradient::Radial: + case QskGradient::Conic: + { + return false; + } + default: + { + return true; } } - - return g; } -#endif - -class QskBoxNodePrivate final : public QSGGeometryNodePrivate -{ - public: - QskBoxNodePrivate() - : geometry( QSGGeometry::defaultAttributes_ColoredPoint2D(), 0 ) - { - } - - QskHashValue metricsHash = 0; - QskHashValue colorsHash = 0; - QRectF rect; - - QSGGeometry geometry; -}; - QskBoxNode::QskBoxNode() - : QSGGeometryNode( *new QskBoxNodePrivate ) { - Q_D( QskBoxNode ); - - setMaterial( qskMaterialVertex ); - setGeometry( &d->geometry ); } QskBoxNode::~QskBoxNode() { - if ( material() != qskMaterialVertex ) - delete material(); } -void QskBoxNode::setBoxData( const QRectF& rect, const QskGradient& fillGradient ) -{ - setBoxData( rect, QskBoxShapeMetrics(), QskBoxBorderMetrics(), - QskBoxBorderColors(), fillGradient ); -} - -void QskBoxNode::setBoxData( const QRectF& rect, +void QskBoxNode::updateNode( const QRectF& rect, const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics, - const QskBoxBorderColors& borderColors, const QskGradient& gradient ) + const QskBoxBorderColors& borderColors, const QskGradient& gradient, + const QskShadowMetrics& shadowMetrics, const QColor& shadowColor ) { - Q_D( QskBoxNode ); + using namespace QskSGNode; + + QskBoxShadowNode* shadowNode = nullptr; + QskBoxRectangleNode* rectNode = nullptr; + QskBoxFillNode* fillNode = nullptr; + + if ( !shadowMetrics.isNull() + && shadowColor.isValid() && shadowColor.alpha() != 0 ) + { + shadowNode = qskNode< QskBoxShadowNode >( this, ShadowRole ); + shadowNode->setShadowData( shadowMetrics.shadowRect( rect ), + shape, shadowMetrics.blurRadius(), shadowColor ); + } /* - QskBoxRenderer supports certain linear gradients only. - For everything else we would have to use a QskGradientMaterial instead. - - As a temporary solution we simply "convert" gradient into a - simple QskLinearGradient so that at least something is happening. - TODO ... - */ -#if 1 - const auto fillGradient = qskEffectiveGradient( gradient ); -#endif - - const auto metricsHash = qskMetricsHash( shape, borderMetrics ); - const auto colorsHash = qskColorsHash( borderColors, fillGradient ); - - if ( ( metricsHash == d->metricsHash ) && - ( colorsHash == d->colorsHash ) && ( rect == d->rect ) ) - { - return; - } - - d->metricsHash = metricsHash; - d->colorsHash = colorsHash; - d->rect = rect; - - markDirty( QSGNode::DirtyMaterial ); - markDirty( QSGNode::DirtyGeometry ); - - if ( rect.isEmpty() ) - { - d->geometry.allocate( 0 ); - return; - } - - bool hasFill = fillGradient.isVisible(); - - bool hasBorder = !borderMetrics.isNull(); - if ( hasBorder ) - { - /* - Wrong as the border width should have an - effect - even if not being visible. TODO ... - */ - - hasBorder = borderColors.isVisible(); - } - - if ( !hasBorder && !hasFill ) - { - d->geometry.allocate( 0 ); - return; - } - - const bool isFillMonochrome = hasFill ? fillGradient.isMonochrome() : true; - const bool isBorderMonochrome = hasBorder ? borderColors.isMonochrome() : true; - - if ( hasFill && hasBorder ) - { - if ( isFillMonochrome && isBorderMonochrome ) - { - if ( borderColors.left().rgbStart() == fillGradient.rgbStart() ) - { - // we can draw border and background in one - hasBorder = false; - } - } - } - -#if 0 - /* - Always using the same material result in a better batching - but wastes some memory. when we have a solid color. - Maybe its worth to introduce a flag to control the behaviour, - but for the moment we go with performance. + QskBoxRectangleNode supports vertical/horizontal/diagonal gradients only. + If our gradient doesn't fall into this category we use a QskBoxFillNode. + However the border is always done with a QskBoxRectangleNode */ - bool maybeFlat = true; - - if ( maybeFlat ) + if ( qskIsBoxGradient( gradient ) ) { - if ( ( hasFill && hasBorder ) || - ( hasFill && !isFillMonochrome ) || - ( hasBorder && !isBorderMonochrome ) ) - { - maybeFlat = false; - } - } -#else - bool maybeFlat = false; -#endif - - QskBoxRenderer renderer; - - if ( !maybeFlat ) - { - setMonochrome( false ); - - renderer.renderBox( d->rect, shape, borderMetrics, - borderColors, fillGradient, *geometry() ); + rectNode = qskNode< QskBoxRectangleNode >( this, BoxRole ); + rectNode->updateNode( rect, shape, borderMetrics, borderColors, gradient ); } else { - // all is done with one color - setMonochrome( true ); - - auto* flatMaterial = static_cast< QSGFlatColorMaterial* >( material() ); - - if ( hasFill ) + if ( !borderMetrics.isNull() && borderColors.isVisible() ) { - flatMaterial->setColor( fillGradient.rgbStart() ); - renderer.renderFill( d->rect, shape, QskBoxBorderMetrics(), *geometry() ); + rectNode = qskNode< QskBoxRectangleNode >( this, BoxRole ); + rectNode->updateNode( rect, shape, borderMetrics, borderColors, QskGradient() ); } - else + + if ( gradient.isVisible() ) { - flatMaterial->setColor( borderColors.left().rgbStart() ); - renderer.renderBorder( d->rect, shape, borderMetrics, *geometry() ); + fillNode = qskNode< QskBoxFillNode >( this, FillRole ); + fillNode->updateNode( rect, shape, borderMetrics, gradient ); } } -} - -void QskBoxNode::setMonochrome( bool on ) -{ - const auto material = this->material(); - - if ( on == ( material != qskMaterialVertex ) ) - return; - - Q_D( QskBoxNode ); - - d->geometry.allocate( 0 ); - - if ( on ) - { - setMaterial( new QSGFlatColorMaterial() ); - - const QSGGeometry g( QSGGeometry::defaultAttributes_Point2D(), 0 ); - memcpy( ( void* ) &d->geometry, ( void* ) &g, sizeof( QSGGeometry ) ); - } - else - { - setMaterial( qskMaterialVertex ); - delete material; - - const QSGGeometry g( QSGGeometry::defaultAttributes_ColoredPoint2D(), 0 ); - memcpy( ( void* ) &d->geometry, ( void* ) &g, sizeof( QSGGeometry ) ); - } + + qskUpdateChildren( this, ShadowRole, shadowNode ); + qskUpdateChildren( this, BoxRole, rectNode ); + qskUpdateChildren( this, FillRole, fillNode ); } diff --git a/src/nodes/QskBoxNode.h b/src/nodes/QskBoxNode.h index 5aa9bab4..05e53910 100644 --- a/src/nodes/QskBoxNode.h +++ b/src/nodes/QskBoxNode.h @@ -9,30 +9,24 @@ #include "QskGlobal.h" #include +class QskShadowMetrics; class QskBoxShapeMetrics; class QskBoxBorderMetrics; class QskBoxBorderColors; class QskGradient; +class QskShadowMetrics; +class QColor; -class QskBoxNodePrivate; - -class QSK_EXPORT QskBoxNode : public QSGGeometryNode +class QSK_EXPORT QskBoxNode : public QSGNode { public: QskBoxNode(); ~QskBoxNode() override; - void setBoxData( const QRectF&, + void updateNode( const QRectF&, const QskBoxShapeMetrics&, const QskBoxBorderMetrics&, - const QskBoxBorderColors&, const QskGradient& ); - - void setBoxData( const QRectF& rect, const QskGradient& ); - - private: - void setMonochrome( bool on ); - - Q_DECLARE_PRIVATE( QskBoxNode ) - + const QskBoxBorderColors&, const QskGradient&, + const QskShadowMetrics&, const QColor& shadowColor ); }; #endif diff --git a/src/nodes/QskBoxRectangleNode.cpp b/src/nodes/QskBoxRectangleNode.cpp new file mode 100644 index 00000000..94f4e238 --- /dev/null +++ b/src/nodes/QskBoxRectangleNode.cpp @@ -0,0 +1,287 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#include "QskBoxRectangleNode.h" +#include "QskBoxBorderColors.h" +#include "QskBoxBorderMetrics.h" +#include "QskBoxRenderer.h" +#include "QskBoxShapeMetrics.h" +#include "QskGradient.h" +#include "QskGradientDirection.h" + +#include +#include +#include + +QSK_QT_PRIVATE_BEGIN +#include +QSK_QT_PRIVATE_END + +Q_GLOBAL_STATIC( QSGVertexColorMaterial, qskMaterialVertex ) + +static inline QskHashValue qskMetricsHash( + const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics ) +{ + QskHashValue hash = 13000; + + hash = shape.hash( hash ); + return borderMetrics.hash( hash ); +} + +static inline QskHashValue qskColorsHash( + const QskBoxBorderColors& borderColors, const QskGradient& fillGradient ) +{ + QskHashValue hash = 13000; + hash = borderColors.hash( hash ); + return fillGradient.hash( hash ); +} + +#if 1 + + +static inline QskGradient qskEffectiveGradient( const QskGradient& gradient ) +{ + QskGradient g; + + if ( gradient.isVisible() ) + { + if ( gradient.isMonochrome() ) + { + g.setLinearDirection( Qt::Vertical ); + g.setStops( gradient.rgbStart() ); + } + else + { + + switch( gradient.type() ) + { + case QskGradient::Linear: + { + auto dir = gradient.linearDirection(); + + if ( dir.isTilted() ) + { + dir.setStart( 0.0, 0.0 ); + dir.setStop( 1.0, 1.0 ); + } + + g.setLinearDirection( dir ); + + break; + } + case QskGradient::Radial: + case QskGradient::Conic: + { + qWarning() << "QskBoxRectangleNode does not support radial/conic gradients"; + g.setLinearDirection( Qt::Vertical ); + + break; + } + case QskGradient::Stops: + { + g.setLinearDirection( Qt::Vertical ); + break; + } + } + + g.setStops( gradient.stops() ); + } + } + + return g; +} + +#endif + +class QskBoxRectangleNodePrivate final : public QSGGeometryNodePrivate +{ + public: + QskBoxRectangleNodePrivate() + : geometry( QSGGeometry::defaultAttributes_ColoredPoint2D(), 0 ) + { + } + + QskHashValue metricsHash = 0; + QskHashValue colorsHash = 0; + QRectF rect; + + QSGGeometry geometry; +}; + +QskBoxRectangleNode::QskBoxRectangleNode() + : QSGGeometryNode( *new QskBoxRectangleNodePrivate ) +{ + Q_D( QskBoxRectangleNode ); + + setMaterial( qskMaterialVertex ); + setGeometry( &d->geometry ); +} + +QskBoxRectangleNode::~QskBoxRectangleNode() +{ + if ( material() != qskMaterialVertex ) + delete material(); +} + +void QskBoxRectangleNode::updateNode( + const QRectF& rect, const QskGradient& fillGradient ) +{ + updateNode( rect, QskBoxShapeMetrics(), QskBoxBorderMetrics(), + QskBoxBorderColors(), fillGradient ); +} + +void QskBoxRectangleNode::updateNode( const QRectF& rect, + const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics, + const QskBoxBorderColors& borderColors, const QskGradient& gradient ) +{ + Q_D( QskBoxRectangleNode ); + + /* + QskBoxRenderer supports certain linear gradients only. + For everything else we would have to use a QskGradientMaterial instead. + + As a temporary solution we simply "convert" gradient into a + simple QskLinearGradient so that at least something is happening. + TODO ... + */ +#if 1 + const auto fillGradient = qskEffectiveGradient( gradient ); +#endif + + const auto metricsHash = qskMetricsHash( shape, borderMetrics ); + const auto colorsHash = qskColorsHash( borderColors, fillGradient ); + + if ( ( metricsHash == d->metricsHash ) && + ( colorsHash == d->colorsHash ) && ( rect == d->rect ) ) + { + return; + } + + d->metricsHash = metricsHash; + d->colorsHash = colorsHash; + d->rect = rect; + + markDirty( QSGNode::DirtyMaterial ); + markDirty( QSGNode::DirtyGeometry ); + + if ( rect.isEmpty() ) + { + d->geometry.allocate( 0 ); + return; + } + + bool hasFill = fillGradient.isVisible(); + + bool hasBorder = !borderMetrics.isNull(); + if ( hasBorder ) + { + /* + Wrong as the border width should have an + effect - even if not being visible. TODO ... + */ + + hasBorder = borderColors.isVisible(); + } + + if ( !hasBorder && !hasFill ) + { + d->geometry.allocate( 0 ); + return; + } + + const bool isFillMonochrome = hasFill ? fillGradient.isMonochrome() : true; + const bool isBorderMonochrome = hasBorder ? borderColors.isMonochrome() : true; + + if ( hasFill && hasBorder ) + { + if ( isFillMonochrome && isBorderMonochrome ) + { + if ( borderColors.left().rgbStart() == fillGradient.rgbStart() ) + { + // we can draw border and background in one + hasBorder = false; + } + } + } + +#if 0 + /* + Always using the same material result in a better batching + but wastes some memory. when we have a solid color. + Maybe its worth to introduce a flag to control the behaviour, + but for the moment we go with performance. + */ + + bool maybeFlat = true; + + if ( maybeFlat ) + { + if ( ( hasFill && hasBorder ) || + ( hasFill && !isFillMonochrome ) || + ( hasBorder && !isBorderMonochrome ) ) + { + maybeFlat = false; + } + } +#else + bool maybeFlat = false; +#endif + + QskBoxRenderer renderer; + + if ( !maybeFlat ) + { + setMonochrome( false ); + + renderer.renderBox( d->rect, shape, borderMetrics, + borderColors, fillGradient, *geometry() ); + } + else + { + // all is done with one color + setMonochrome( true ); + + auto* flatMaterial = static_cast< QSGFlatColorMaterial* >( material() ); + + if ( hasFill ) + { + flatMaterial->setColor( fillGradient.rgbStart() ); + renderer.renderFill( d->rect, shape, QskBoxBorderMetrics(), *geometry() ); + } + else + { + flatMaterial->setColor( borderColors.left().rgbStart() ); + renderer.renderBorder( d->rect, shape, borderMetrics, *geometry() ); + } + } +} + +void QskBoxRectangleNode::setMonochrome( bool on ) +{ + const auto material = this->material(); + + if ( on == ( material != qskMaterialVertex ) ) + return; + + Q_D( QskBoxRectangleNode ); + + d->geometry.allocate( 0 ); + + if ( on ) + { + setMaterial( new QSGFlatColorMaterial() ); + + const QSGGeometry g( QSGGeometry::defaultAttributes_Point2D(), 0 ); + memcpy( ( void* ) &d->geometry, ( void* ) &g, sizeof( QSGGeometry ) ); + } + else + { + setMaterial( qskMaterialVertex ); + delete material; + + const QSGGeometry g( QSGGeometry::defaultAttributes_ColoredPoint2D(), 0 ); + memcpy( ( void* ) &d->geometry, ( void* ) &g, sizeof( QSGGeometry ) ); + } +} diff --git a/src/nodes/QskShadedBoxNode.h b/src/nodes/QskBoxRectangleNode.h similarity index 51% rename from src/nodes/QskShadedBoxNode.h rename to src/nodes/QskBoxRectangleNode.h index c4a67ee0..131712a0 100644 --- a/src/nodes/QskShadedBoxNode.h +++ b/src/nodes/QskBoxRectangleNode.h @@ -3,30 +3,36 @@ * This file may be used under the terms of the QSkinny License, Version 1.0 *****************************************************************************/ -#ifndef QSK_SHADED_BOX_NODE_H -#define QSK_SHADED_BOX_NODE_H +#ifndef QSK_BOX_RECTANGLE_NODE_H +#define QSK_BOX_RECTANGLE_NODE_H #include "QskGlobal.h" #include -class QskShadowMetrics; class QskBoxShapeMetrics; class QskBoxBorderMetrics; class QskBoxBorderColors; class QskGradient; -class QskShadowMetrics; -class QColor; -class QSK_EXPORT QskShadedBoxNode : public QSGNode +class QskBoxRectangleNodePrivate; + +class QSK_EXPORT QskBoxRectangleNode : public QSGGeometryNode { public: - QskShadedBoxNode(); - ~QskShadedBoxNode() override; + QskBoxRectangleNode(); + ~QskBoxRectangleNode() override; - void setBoxData( const QRectF&, + void updateNode( const QRectF&, const QskBoxShapeMetrics&, const QskBoxBorderMetrics&, - const QskBoxBorderColors&, const QskGradient&, - const QskShadowMetrics&, const QColor& shadowColor ); + const QskBoxBorderColors&, const QskGradient& ); + + void updateNode( const QRectF& rect, const QskGradient& ); + + private: + void setMonochrome( bool on ); + + Q_DECLARE_PRIVATE( QskBoxRectangleNode ) + }; #endif diff --git a/src/nodes/QskShadedBoxNode.cpp b/src/nodes/QskShadedBoxNode.cpp deleted file mode 100644 index 76936d84..00000000 --- a/src/nodes/QskShadedBoxNode.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) 2016 Uwe Rathmann - * This file may be used under the terms of the QSkinny License, Version 1.0 - *****************************************************************************/ - -#include "QskShadedBoxNode.h" -#include "QskBoxFillNode.h" -#include "QskBoxShadowNode.h" -#include "QskBoxNode.h" -#include "QskSGNode.h" - -#include "QskGradient.h" -#include "QskGradientDirection.h" -#include "QskShadowMetrics.h" -#include "QskBoxBorderMetrics.h" -#include "QskBoxBorderColors.h" - -namespace -{ - enum Role - { - ShadowRole, - BoxRole, - FillRole - }; -} - -void qskUpdateChildren( QSGNode* parentNode, quint8 role, QSGNode* node ) -{ - static const QVector< quint8 > roles = { ShadowRole, BoxRole, FillRole }; - - auto oldNode = QskSGNode::findChildNode( parentNode, role ); - QskSGNode::replaceChildNode( roles, role, parentNode, oldNode, node ); -} - -template< typename Node > -inline Node* qskNode( QSGNode* parentNode, quint8 role ) -{ - using namespace QskSGNode; - - auto node = static_cast< Node* > ( findChildNode( parentNode, role ) ); - - if ( node == nullptr ) - { - node = new Node(); - setNodeRole( node, role ); - } - - return node; -} - -static inline bool qskIsBoxGradient( const QskGradient& gradient ) -{ - if ( !gradient.isVisible() || gradient.isMonochrome() ) - return true; - - switch( gradient.type() ) - { - case QskGradient::Linear: - { - auto dir = gradient.linearDirection(); - - if ( dir.isTilted() ) - { - // only diagonal from topLeft to bottomRight - return ( dir.x1() == dir.x2() ) && ( dir.y1() == dir.y2() ); - } - - return true; - } - case QskGradient::Radial: - case QskGradient::Conic: - { - return false; - } - default: - { - return true; - } - } -} - -QskShadedBoxNode::QskShadedBoxNode() -{ -} - -QskShadedBoxNode::~QskShadedBoxNode() -{ -} - -void QskShadedBoxNode::setBoxData( const QRectF& rect, - const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics, - const QskBoxBorderColors& borderColors, const QskGradient& gradient, - const QskShadowMetrics& shadowMetrics, const QColor& shadowColor ) -{ - using namespace QskSGNode; - - QskBoxShadowNode* shadowNode = nullptr; - QskBoxNode* boxNode = nullptr; - QskBoxFillNode* fillNode = nullptr; - - if ( !shadowMetrics.isNull() - && shadowColor.isValid() && shadowColor.alpha() != 0 ) - { - shadowNode = qskNode< QskBoxShadowNode >( this, ShadowRole ); - shadowNode->setShadowData( shadowMetrics.shadowRect( rect ), - shape, shadowMetrics.blurRadius(), shadowColor ); - } - - /* - QskBoxNode supports vertical/horizontal/diagonal gradients only. - If our gradient doesn't fall into this category we use a QskBoxFillNode. - However the border is always done with a QskBoxNode - */ - - if ( qskIsBoxGradient( gradient ) ) - { - boxNode = qskNode< QskBoxNode >( this, BoxRole ); - boxNode->setBoxData( rect, shape, borderMetrics, borderColors, gradient ); - } - else - { - if ( !borderMetrics.isNull() && borderColors.isVisible() ) - { - boxNode = qskNode< QskBoxNode >( this, BoxRole ); - boxNode->setBoxData( rect, shape, borderMetrics, borderColors, QskGradient() ); - } - - if ( gradient.isVisible() ) - { - fillNode = qskNode< QskBoxFillNode >( this, FillRole ); - fillNode->updateNode( rect, shape, borderMetrics, gradient ); - } - } - - qskUpdateChildren( this, ShadowRole, shadowNode ); - qskUpdateChildren( this, BoxRole, boxNode ); - qskUpdateChildren( this, FillRole, fillNode ); -} diff --git a/src/src.pro b/src/src.pro index 04c48aa8..dce7e265 100644 --- a/src/src.pro +++ b/src/src.pro @@ -104,6 +104,7 @@ HEADERS += \ nodes/QskBoxNode.h \ nodes/QskBoxClipNode.h \ nodes/QskBoxFillNode.h \ + nodes/QskBoxRectangleNode.h \ nodes/QskBoxRenderer.h \ nodes/QskBoxRendererColorMap.h \ nodes/QskBoxShadowNode.h \ @@ -114,7 +115,6 @@ HEADERS += \ nodes/QskRichTextRenderer.h \ nodes/QskScaleRenderer.h \ nodes/QskSGNode.h \ - nodes/QskShadedBoxNode.h \ nodes/QskStrokeNode.h \ nodes/QskShapeNode.h \ nodes/QskGradientMaterial.h \ @@ -130,6 +130,7 @@ SOURCES += \ nodes/QskBoxNode.cpp \ nodes/QskBoxClipNode.cpp \ nodes/QskBoxFillNode.cpp \ + nodes/QskBoxRectangleNode.cpp \ nodes/QskBoxRendererRect.cpp \ nodes/QskBoxRendererEllipse.cpp \ nodes/QskBoxRendererDEllipse.cpp \ @@ -141,7 +142,6 @@ SOURCES += \ nodes/QskRichTextRenderer.cpp \ nodes/QskScaleRenderer.cpp \ nodes/QskSGNode.cpp \ - nodes/QskShadedBoxNode.cpp \ nodes/QskStrokeNode.cpp \ nodes/QskShapeNode.cpp \ nodes/QskGradientMaterial.cpp \