From 68cca6a53ccff35400cfecbbaaf344af817565ad Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Wed, 15 Nov 2023 11:47:56 +0100 Subject: [PATCH] QskBoxFillNode introduced --- src/CMakeLists.txt | 6 + src/nodes/QskBoxClipNode.cpp | 1 + src/nodes/QskBoxFillNode.cpp | 63 ++------- src/nodes/QskBoxFillNode.h | 6 +- src/nodes/QskBoxRectangleNode.cpp | 98 +++---------- src/nodes/QskBoxRectangleNode.h | 8 +- src/nodes/QskBoxShadowNode.cpp | 1 + src/nodes/QskFillNode.cpp | 153 +++++++++++++++++++++ src/nodes/QskFillNode.h | 50 +++++++ src/nodes/QskFillNodePrivate.h | 32 +++++ src/nodes/QskLinesNode.cpp | 47 ++++++- src/nodes/QskLinesNode.h | 6 + src/nodes/QskRectangleNode.cpp | 94 ++----------- src/nodes/QskRectangleNode.h | 8 +- src/nodes/QskSGNode.cpp | 12 +- src/nodes/QskShapeNode.cpp | 75 ++-------- src/nodes/QskShapeNode.h | 6 +- src/nodes/QskStrokeNode.cpp | 221 +++++------------------------- src/nodes/QskStrokeNode.h | 30 +--- src/nodes/QskTickmarksNode.cpp | 1 - 20 files changed, 418 insertions(+), 500 deletions(-) create mode 100644 src/nodes/QskFillNode.cpp create mode 100644 src/nodes/QskFillNode.h create mode 100644 src/nodes/QskFillNodePrivate.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a2f36047..b626e406 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -108,6 +108,7 @@ list(APPEND HEADERS nodes/QskBoxColorMap.h nodes/QskBoxShadowNode.h nodes/QskColorRamp.h + nodes/QskFillNode.h nodes/QskGraphicNode.h nodes/QskLinesNode.h nodes/QskPaintedNode.h @@ -126,6 +127,10 @@ list(APPEND HEADERS nodes/QskVertex.h ) +list(APPEND PRIVATE_HEADERS + nodes/QskFillNodePrivate.h +) + list(APPEND SOURCES nodes/QskArcNode.cpp nodes/QskBoxNode.cpp @@ -138,6 +143,7 @@ list(APPEND SOURCES nodes/QskBoxGradientStroker.cpp nodes/QskBoxShadowNode.cpp nodes/QskColorRamp.cpp + nodes/QskFillNode.cpp nodes/QskGraphicNode.cpp nodes/QskLinesNode.cpp nodes/QskPaintedNode.cpp diff --git a/src/nodes/QskBoxClipNode.cpp b/src/nodes/QskBoxClipNode.cpp index fe1fe4fc..7edf1496 100644 --- a/src/nodes/QskBoxClipNode.cpp +++ b/src/nodes/QskBoxClipNode.cpp @@ -76,5 +76,6 @@ void QskBoxClipNode::setBox( const QRectF& rect, */ setClipRect( qskValidOrEmptyInnerRect( rect, border.widths() ) ); + m_geometry.markVertexDataDirty(); markDirty( QSGNode::DirtyGeometry ); } diff --git a/src/nodes/QskBoxFillNode.cpp b/src/nodes/QskBoxFillNode.cpp index 1c3099e9..078e4afe 100644 --- a/src/nodes/QskBoxFillNode.cpp +++ b/src/nodes/QskBoxFillNode.cpp @@ -10,13 +10,7 @@ #include "QskBoxShapeMetrics.h" #include "QskBoxBorderMetrics.h" #include "QskBoxRenderer.h" -#include "QskSGNode.h" - -#include - -QSK_QT_PRIVATE_BEGIN -#include -QSK_QT_PRIVATE_END +#include "QskFillNodePrivate.h" static inline QskHashValue qskMetricsHash( const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics ) @@ -27,14 +21,9 @@ static inline QskHashValue qskMetricsHash( return borderMetrics.hash( hash ); } -class QskBoxFillNodePrivate final : public QSGGeometryNodePrivate +class QskBoxFillNodePrivate final : public QskFillNodePrivate { public: - QskBoxFillNodePrivate() - : geometry( QSGGeometry::defaultAttributes_Point2D(), 0 ) - { - } - inline void resetValues() { rect = QRectF(); @@ -46,19 +35,11 @@ class QskBoxFillNodePrivate final : public QSGGeometryNodePrivate QskHashValue gradientHash = 0; QskHashValue metricsHash = 0; - - QSGGeometry geometry; - int gradientType = -1; }; QskBoxFillNode::QskBoxFillNode() - : QSGGeometryNode( *new QskBoxFillNodePrivate ) + : QskFillNode( *new QskBoxFillNodePrivate ) { - Q_D( QskBoxFillNode ); - - setGeometry( &d->geometry ); - setMaterial( new QSGFlatColorMaterial() ); - setFlag( QSGNode::OwnsMaterial, true ); } void QskBoxFillNode::updateNode( const QRectF& rect, const QskGradient& gradient ) @@ -75,7 +56,7 @@ void QskBoxFillNode::updateNode( if ( rect.isEmpty() || !gradient.isVisible() ) { d->resetValues(); - QskSGNode::resetGeometry( this ); + resetGeometry(); return; } @@ -92,48 +73,22 @@ void QskBoxFillNode::updateNode( if ( dirtyMetrics ) { - QskBox::renderFillGeometry( rect, shapeMetrics, borderMetrics, d->geometry ); + QskBox::renderFillGeometry( rect, shapeMetrics, borderMetrics, *geometry() ); + markDirty( QSGNode::DirtyGeometry ); + geometry()->markVertexDataDirty(); } if ( gradient.isMonochrome() ) { if ( dirtyColors ) - { - if ( material() == nullptr || d->gradientType >= 0 ) - { - setMaterial( new QSGFlatColorMaterial() ); - d->gradientType = -1; - } - - const auto color = gradient.startColor().toRgb(); - - auto mat = static_cast< QSGFlatColorMaterial* >( material() ); - if ( mat->color() != color ) - { - mat->setColor( color ); - markDirty( QSGNode::DirtyMaterial ); - } - } + setColoring( gradient.startColor().toRgb() ); } else { // dirtyMetrics: the shader also depends on the target rectangle ! if ( dirtyColors || dirtyMetrics ) - { - const auto effectiveGradient = gradient.effectiveGradient(); - const auto gradientType = effectiveGradient.type(); - - if ( ( material() == nullptr ) || ( gradientType != d->gradientType ) ) - { - setMaterial( QskGradientMaterial::createMaterial( gradientType ) ); - d->gradientType = gradientType; - } - - auto mat = static_cast< QskGradientMaterial* >( material() ); - if ( mat->updateGradient( rect, effectiveGradient ) ) - markDirty( QSGNode::DirtyMaterial ); - } + setColoring( rect, gradient.effectiveGradient() ); } } diff --git a/src/nodes/QskBoxFillNode.h b/src/nodes/QskBoxFillNode.h index 7ba749bd..9b8875fe 100644 --- a/src/nodes/QskBoxFillNode.h +++ b/src/nodes/QskBoxFillNode.h @@ -7,7 +7,7 @@ #define QSK_BOX_FILL_NODE_H #include "QskGlobal.h" -#include +#include "QskFillNode.h" class QskGradient; class QskBoxShapeMetrics; @@ -15,8 +15,10 @@ class QskBoxBorderMetrics; class QskBoxFillNodePrivate; -class QSK_EXPORT QskBoxFillNode : public QSGGeometryNode +class QSK_EXPORT QskBoxFillNode : public QskFillNode { + using Inherited = QskFillNode; + public: QskBoxFillNode(); diff --git a/src/nodes/QskBoxRectangleNode.cpp b/src/nodes/QskBoxRectangleNode.cpp index 41fd2d5f..9177a503 100644 --- a/src/nodes/QskBoxRectangleNode.cpp +++ b/src/nodes/QskBoxRectangleNode.cpp @@ -10,16 +10,7 @@ #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, qskMaterialColorVertex ) +#include "QskFillNodePrivate.h" static inline QskHashValue qskMetricsHash( const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics ) @@ -55,34 +46,21 @@ static inline QskGradient qskEffectiveGradient( const QskGradient& gradient ) #endif -class QskBoxRectangleNodePrivate final : public QSGGeometryNodePrivate +class QskBoxRectangleNodePrivate final : public QskFillNodePrivate { public: - QskBoxRectangleNodePrivate() - : geometry( QSGGeometry::defaultAttributes_ColoredPoint2D(), 0 ) - { - } - QskHashValue metricsHash = 0; QskHashValue colorsHash = 0; QRectF rect; - - QSGGeometry geometry; }; QskBoxRectangleNode::QskBoxRectangleNode() - : QSGGeometryNode( *new QskBoxRectangleNodePrivate ) + : QskFillNode( *new QskBoxRectangleNodePrivate ) { - Q_D( QskBoxRectangleNode ); - - setMaterial( qskMaterialColorVertex ); - setGeometry( &d->geometry ); } QskBoxRectangleNode::~QskBoxRectangleNode() { - if ( material() != qskMaterialColorVertex ) - delete material(); } void QskBoxRectangleNode::updateNode( @@ -129,7 +107,7 @@ void QskBoxRectangleNode::updateNode( const QRectF& rect, if ( rect.isEmpty() ) { - d->geometry.allocate( 0 ); + resetGeometry(); return; } @@ -148,7 +126,7 @@ void QskBoxRectangleNode::updateNode( const QRectF& rect, if ( !hasBorder && !hasFill ) { - d->geometry.allocate( 0 ); + resetGeometry(); return; } @@ -167,6 +145,8 @@ void QskBoxRectangleNode::updateNode( const QRectF& rect, } } + auto coloring = QskFillNode::Polychrome; + #if 0 /* Always using the same material result in a better batching @@ -175,74 +155,40 @@ void QskBoxRectangleNode::updateNode( const QRectF& rect, but for the moment we go with performance. */ - bool maybeFlat = true; - - if ( maybeFlat ) + if ( !( hasFill && hasBorder ) ) { - if ( ( hasFill && hasBorder ) || - ( hasFill && !isFillMonochrome ) || - ( hasBorder && !isBorderMonochrome ) ) + if ( ( hasFill && isFillMonochrome ) + || ( hasBorder && !isBorderMonochrome ) { - maybeFlat = false; + coloring = QskFillNode::Monochrome; } } -#else - bool maybeFlat = false; #endif - if ( !maybeFlat ) + auto& geometry = *this->geometry(); + + if ( coloring == QskFillNode::Polychrome ) { - setMonochrome( false ); + setColoring( coloring ); QskBox::renderBox( d->rect, shape, borderMetrics, - borderColors, fillGradient, *geometry() ); + borderColors, fillGradient, geometry ); } else { - // all is done with one color - setMonochrome( true ); - - auto* flatMaterial = static_cast< QSGFlatColorMaterial* >( material() ); - if ( hasFill ) { - flatMaterial->setColor( fillGradient.rgbStart() ); + setColoring( fillGradient.rgbStart() ); QskBox::renderFillGeometry( - d->rect, shape, QskBoxBorderMetrics(), *geometry() ); + d->rect, shape, QskBoxBorderMetrics(), geometry ); } else { - flatMaterial->setColor( borderColors.left().rgbStart() ); + setColoring( borderColors.left().rgbStart() ); QskBox::renderBorderGeometry( - d->rect, shape, borderMetrics, *geometry() ); + d->rect, shape, borderMetrics, geometry ); } } -} - -void QskBoxRectangleNode::setMonochrome( bool on ) -{ - const auto material = this->material(); - - if ( on == ( material != qskMaterialColorVertex ) ) - 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( qskMaterialColorVertex ); - delete material; - - const QSGGeometry g( QSGGeometry::defaultAttributes_ColoredPoint2D(), 0 ); - memcpy( ( void* ) &d->geometry, ( void* ) &g, sizeof( QSGGeometry ) ); - } + + geometry.markVertexDataDirty(); } diff --git a/src/nodes/QskBoxRectangleNode.h b/src/nodes/QskBoxRectangleNode.h index 1fa9dee3..502f5ced 100644 --- a/src/nodes/QskBoxRectangleNode.h +++ b/src/nodes/QskBoxRectangleNode.h @@ -7,7 +7,7 @@ #define QSK_BOX_RECTANGLE_NODE_H #include "QskGlobal.h" -#include +#include "QskFillNode.h" class QskBoxShapeMetrics; class QskBoxBorderMetrics; @@ -16,8 +16,10 @@ class QskGradient; class QskBoxRectangleNodePrivate; -class QSK_EXPORT QskBoxRectangleNode : public QSGGeometryNode +class QSK_EXPORT QskBoxRectangleNode : public QskFillNode { + using Inherited = QskFillNode; + public: QskBoxRectangleNode(); ~QskBoxRectangleNode() override; @@ -32,8 +34,6 @@ class QSK_EXPORT QskBoxRectangleNode : public QSGGeometryNode const QskBoxShapeMetrics&, const QskGradient& ); private: - void setMonochrome( bool on ); - Q_DECLARE_PRIVATE( QskBoxRectangleNode ) }; diff --git a/src/nodes/QskBoxShadowNode.cpp b/src/nodes/QskBoxShadowNode.cpp index 9740fb23..d94d8ad3 100644 --- a/src/nodes/QskBoxShadowNode.cpp +++ b/src/nodes/QskBoxShadowNode.cpp @@ -281,6 +281,7 @@ void QskBoxShadowNode::setShadowData( QSGGeometry::updateTexturedRectGeometry( &d->geometry, d->rect, QRectF( -0.5, -0.5, 1.0, 1.0 ) ); + d->geometry.markVertexDataDirty(); markDirty( QSGNode::DirtyGeometry ); QVector2D aspect( 1.0, 1.0 ); diff --git a/src/nodes/QskFillNode.cpp b/src/nodes/QskFillNode.cpp new file mode 100644 index 00000000..b5fbba51 --- /dev/null +++ b/src/nodes/QskFillNode.cpp @@ -0,0 +1,153 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskFillNode.h" +#include "QskGradientMaterial.h" +#include "QskFillNodePrivate.h" +#include "QskSGNode.h" + +#include +#include +#include + +Q_GLOBAL_STATIC( QSGVertexColorMaterial, qskMaterialColorVertex ) + +static inline QskGradient::Type qskGradientType( QskFillNode::Coloring coloring ) +{ + const auto type = QskGradient::Linear + coloring - QskFillNode::Linear; + return static_cast< QskGradient::Type >( type ); +} + +static inline QskFillNode::Coloring qskColoring( QskGradient::Type type ) +{ + const auto coloring = QskFillNode::Linear + type - QskGradient::Linear; + return static_cast< QskFillNode::Coloring >( coloring ); +} + +QskFillNode::QskFillNode() + : QskFillNode( *new QskFillNodePrivate ) +{ +} + +QskFillNode::QskFillNode( QskFillNodePrivate& dd ) + : QSGGeometryNode( dd ) +{ + setGeometry( &dd.geometry ); + + setMaterial( qskMaterialColorVertex ); + setFlag( QSGNode::OwnsMaterial, false ); +} + +QskFillNode::~QskFillNode() +{ +} + +void QskFillNode::resetGeometry() +{ + QskSGNode::resetGeometry( this ); +} + +void QskFillNode::setColoring( Coloring coloring ) +{ + Q_D( QskFillNode ); + + if ( coloring == d->coloring ) + return; + + d->coloring = coloring; + + switch( coloring ) + { + case Monochrome: + { + setMaterial( new QSGFlatColorMaterial() ); + break; + } + + case Polychrome: + { + setMaterial( qskMaterialColorVertex ); + break; + } + + /* + QSGDefaultInternalRectangleNode is using QSGSmoothColorMaterial. + Can we do something useful with it too ? TODO ... + */ + + default: + { + const auto gradientType = qskGradientType( coloring ); + setMaterial( QskGradientMaterial::createMaterial( gradientType ) ); + } + } + + if ( material() == qskMaterialColorVertex ) + { + /* + All color information is stored in the geometry and we can share + the material regardless of the specific colors. + + As nodes with the same material can be batched it might make sense + to use this type of coloring for monochrome fillings: memory vs. performance. + */ + setFlag( QSGNode::OwnsMaterial, false ); // shared: do not delete + + if ( !isGeometryColored() ) + { + const QSGGeometry g( QSGGeometry::defaultAttributes_ColoredPoint2D(), 0 ); + memcpy( ( void* ) &d->geometry, ( void* ) &g, sizeof( QSGGeometry ) ); + } + } + else + { + setFlag( QSGNode::OwnsMaterial, true ); + + if ( isGeometryColored() ) + { + const QSGGeometry g( QSGGeometry::defaultAttributes_Point2D(), 0 ); + memcpy( ( void* ) &d->geometry, ( void* ) &g, sizeof( QSGGeometry ) ); + } + } +} + +QskFillNode::Coloring QskFillNode::coloring() const +{ + return d_func()->coloring; +} + +void QskFillNode::setColoring( const QColor& color ) +{ + setColoring( Monochrome ); + + auto mat = static_cast< QSGFlatColorMaterial* >( material() ); + if ( mat->color() != color ) + { + mat->setColor( color ); + markDirty( QSGNode::DirtyMaterial ); + } +} + +void QskFillNode::setColoring( const QRectF& rect, const QskGradient& gradient ) +{ + if ( gradient.isMonochrome() ) + { + setColoring( gradient.startColor().toRgb() ); + } + else + { + const auto effectiveGradient = gradient.effectiveGradient(); + setColoring( qskColoring( effectiveGradient.type() ) ); + + auto mat = static_cast< QskGradientMaterial* >( material() ); + if ( mat->updateGradient( rect, effectiveGradient ) ) + markDirty( QSGNode::DirtyMaterial ); + } +} + +bool QskFillNode::isGeometryColored() const +{ + return d_func()->geometry.attributeCount() != 1; +} diff --git a/src/nodes/QskFillNode.h b/src/nodes/QskFillNode.h new file mode 100644 index 00000000..5da3be54 --- /dev/null +++ b/src/nodes/QskFillNode.h @@ -0,0 +1,50 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_FILL_NODE_H +#define QSK_FILL_NODE_H + +#include "QskGlobal.h" +#include + +class QskFillNodePrivate; +class QskGradient; + +class QSK_EXPORT QskFillNode : public QSGGeometryNode +{ + using Inherited = QSGGeometryNode; + + public: + enum Coloring + { + Monochrome, + Polychrome, + + Linear, + Radial, + Conic + }; + + QskFillNode(); + ~QskFillNode() override; + + void resetGeometry(); + + void setColoring( Coloring ); + Coloring coloring() const; + + void setColoring( const QColor& ); + void setColoring( const QRectF&, const QskGradient& ); + + bool isGeometryColored() const; + + protected: + QskFillNode( QskFillNodePrivate& ); + + private: + Q_DECLARE_PRIVATE( QskFillNode ) +}; + +#endif diff --git a/src/nodes/QskFillNodePrivate.h b/src/nodes/QskFillNodePrivate.h new file mode 100644 index 00000000..7e79984c --- /dev/null +++ b/src/nodes/QskFillNodePrivate.h @@ -0,0 +1,32 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_FILL_NODE_PRIVATE_H +#define QSK_FILL_NODE_PRIVATE_H + +#include "QskGlobal.h" + +QSK_QT_PRIVATE_BEGIN +#include +QSK_QT_PRIVATE_END + +class QskFillNode; + +class QskFillNodePrivate : public QSGGeometryNodePrivate +{ + public: + inline QskFillNodePrivate() + : geometry( QSGGeometry::defaultAttributes_ColoredPoint2D(), 0 ) + { + } + + private: + friend class QskFillNode; + + QSGGeometry geometry; + QskFillNode::Coloring coloring = QskFillNode::Polychrome; +}; + +#endif diff --git a/src/nodes/QskLinesNode.cpp b/src/nodes/QskLinesNode.cpp index e378776a..7f1e50dd 100644 --- a/src/nodes/QskLinesNode.cpp +++ b/src/nodes/QskLinesNode.cpp @@ -121,7 +121,6 @@ class QskLinesNodePrivate final : public QSGGeometryNodePrivate : geometry( QSGGeometry::defaultAttributes_Point2D(), 0 ) { geometry.setDrawingMode( QSGGeometry::DrawLines ); - geometry.setVertexDataPattern( QSGGeometry::StaticPattern ); } inline qreal round( bool isHorizontal, qreal v ) const @@ -298,6 +297,7 @@ void QskLinesNode::updateLines( const QColor& color, { updateGeometry( stippleMetrics, transform, count, lines ); + d->geometry.markVertexDataDirty(); markDirty( QSGNode::DirtyGeometry ); d->dirty = false; } @@ -336,6 +336,7 @@ void QskLinesNode::updateGrid( const QColor& color, { updateGeometry( stippleMetrics, transform, rect, xValues, yValues ); + d->geometry.markVertexDataDirty(); markDirty( QSGNode::DirtyGeometry ); d->dirty = false; } @@ -388,7 +389,6 @@ void QskLinesNode::updateGeometry( const QskStippleMetrics& stippleMetrics, count, lines, stippleMetrics, points ); } - Q_ASSERT( geom.vertexCount() == ( points - geom.vertexDataAsPoint2D() ) ); } @@ -542,3 +542,46 @@ QSGGeometry::Point2D* QskLinesNode::setSolidLines( return reinterpret_cast< QSGGeometry::Point2D* >( lines ); } + +void QskLinesNode::updatePolygon( const QColor& color, qreal lineWidth, + const QTransform& transform, const QPolygonF& polygon ) +{ + if ( polygon.isEmpty() || !color.isValid() || ( color.alpha() == 0 ) ) + { + QskSGNode::resetGeometry( this ); + return; + } + + Q_D( QskLinesNode ); + + if ( true ) // for the moment we always update the geometry. TODO ... + { + d->geometry.allocate( polygon.count() + 1 ); + + auto points = d->geometry.vertexDataAsPoint2D(); + + if ( transform.isIdentity() ) + { + for ( int i = 0; i < polygon.count(); i++ ) + { + const auto& pos = polygon[i]; + points[i].set( pos.x(), pos.y() ); + } + } + else + { + for ( int i = 0; i < polygon.count(); i++ ) + { + const auto pos = transform.map( polygon[i] ); + points[i].set( pos.x(), pos.y() ); + } + } + + points[ polygon.count() ] = points[0]; + + d->geometry.markVertexDataDirty(); + markDirty( QSGNode::DirtyGeometry ); + } + + d->setLineAttributes( this, color, lineWidth ); +} diff --git a/src/nodes/QskLinesNode.h b/src/nodes/QskLinesNode.h index 711f759d..54e25b9c 100644 --- a/src/nodes/QskLinesNode.h +++ b/src/nodes/QskLinesNode.h @@ -16,6 +16,7 @@ class QskStippleMetrics; class QTransform; class QPointF; class QLineF; +class QPolygonF; class QQuickItem; class QskLinesNodePrivate; @@ -26,6 +27,8 @@ class QskLinesNodePrivate; */ class QSK_EXPORT QskLinesNode : public QSGGeometryNode { + using Inherited = QSGGeometryNode; + public: QskLinesNode(); ~QskLinesNode() override; @@ -53,6 +56,9 @@ class QSK_EXPORT QskLinesNode : public QSGGeometryNode void updateLines( const QColor&, qreal lineWidth, const QskStippleMetrics&, const QTransform&, const QVector< QLineF >& ); + void updatePolygon( const QColor&, qreal lineWidth, + const QTransform&, const QPolygonF& ); + private: void updateLines( const QColor&, qreal lineWidth, const QskStippleMetrics&, const QTransform&, int count, const QLineF* ); diff --git a/src/nodes/QskRectangleNode.cpp b/src/nodes/QskRectangleNode.cpp index 528df7e8..6e51ff29 100644 --- a/src/nodes/QskRectangleNode.cpp +++ b/src/nodes/QskRectangleNode.cpp @@ -5,28 +5,13 @@ #include "QskRectangleNode.h" #include "QskGradient.h" -#include "QskSGNode.h" #include "QskBoxRenderer.h" #include "QskBoxShapeMetrics.h" -#include "QskGradientMaterial.h" +#include "QskFillNodePrivate.h" -#include -#include - -QSK_QT_PRIVATE_BEGIN -#include -QSK_QT_PRIVATE_END - -Q_GLOBAL_STATIC( QSGVertexColorMaterial, qskMaterialColorVertex ) - -class QskRectangleNodePrivate final : public QSGGeometryNodePrivate +class QskRectangleNodePrivate final : public QskFillNodePrivate { public: - QskRectangleNodePrivate() - : geometry( QSGGeometry::defaultAttributes_ColoredPoint2D(), 0 ) - { - } - inline void resetValues() { rect = QRectF(); @@ -35,24 +20,16 @@ class QskRectangleNodePrivate final : public QSGGeometryNodePrivate metricsHash = 0; } - QSGGeometry geometry; - QRectF rect; QskBoxShapeMetrics shape; QskHashValue gradientHash = 0; QskHashValue metricsHash = 0; - - int gradientType = -1; }; QskRectangleNode::QskRectangleNode() - : QSGGeometryNode( *new QskRectangleNodePrivate ) + : QskFillNode( *new QskRectangleNodePrivate ) { - Q_D( QskRectangleNode ); - - setFlag( OwnsMaterial, true ); - setGeometry( &d->geometry ); } QskRectangleNode::~QskRectangleNode() @@ -73,7 +50,7 @@ void QskRectangleNode::updateNode( if ( rect.isEmpty() || !gradient.isVisible() ) { d->resetValues(); - QskSGNode::resetGeometry( this ); + resetGeometry(); return; } @@ -97,39 +74,8 @@ void QskRectangleNode::updateNode( if ( QskBox::isGradientSupported( effectiveShape, effectiveGradient ) ) { - if ( material() != qskMaterialColorVertex ) - { - setMaterial( qskMaterialColorVertex ); - setFlag( OwnsMaterial, false ); + setColoring( Polychrome ); - d->gradientType = -1; - } - - if ( d->geometry.attributeCount() == 1 ) - { - const QSGGeometry g( QSGGeometry::defaultAttributes_ColoredPoint2D(), 0 ); - memcpy( ( void* ) &d->geometry, ( void* ) &g, sizeof( QSGGeometry ) ); - } - } - else - { - if ( material() == qskMaterialColorVertex ) - { - setMaterial( nullptr ); - setFlag( OwnsMaterial, true ); - - d->gradientType = -1; - } - - if ( d->geometry.attributeCount() != 1 ) - { - const QSGGeometry g( QSGGeometry::defaultAttributes_Point2D(), 0 ); - memcpy( ( void* ) &d->geometry, ( void* ) &g, sizeof( QSGGeometry ) ); - } - } - - if ( material() == qskMaterialColorVertex ) - { /* Colors are added to the vertices, while the material does not depend on the gradient at all @@ -137,39 +83,23 @@ void QskRectangleNode::updateNode( if ( dirtyMetrics || dirtyColors ) { QskBox::renderBox( rect, - effectiveShape, effectiveGradient, d->geometry ); + effectiveShape, effectiveGradient, *geometry() ); + geometry()->markVertexDataDirty(); markDirty( QSGNode::DirtyGeometry ); } } else { - /* - Colors are added by the shaders + if ( dirtyColors || dirtyMetrics ) + setColoring( rect, effectiveGradient ); - Monochrome gradients or QskGradient::Stops are supported by the - QskBoxRenderer. So we don't need to handle them here. - */ if ( dirtyMetrics ) { - QskBox::renderFillGeometry( rect, effectiveShape, d->geometry ); + QskBox::renderFillGeometry( rect, effectiveShape, *geometry() ); + + geometry()->markVertexDataDirty(); markDirty( QSGNode::DirtyGeometry ); } - - // dirtyMetrics: the shader also depends on the target rectangle ! - if ( dirtyColors || dirtyMetrics ) - { - const auto gradientType = effectiveGradient.type(); - - if ( gradientType != d->gradientType ) - { - setMaterial( QskGradientMaterial::createMaterial( gradientType ) ); - d->gradientType = gradientType; - } - - auto mat = static_cast< QskGradientMaterial* >( material() ); - if ( mat->updateGradient( rect, effectiveGradient ) ) - markDirty( QSGNode::DirtyMaterial ); - } } } diff --git a/src/nodes/QskRectangleNode.h b/src/nodes/QskRectangleNode.h index dced2515..5bcbec20 100644 --- a/src/nodes/QskRectangleNode.h +++ b/src/nodes/QskRectangleNode.h @@ -7,7 +7,7 @@ #define QSK_RECTANGLE_NODE_H #include "QskGlobal.h" -#include +#include "QskFillNode.h" class QskGradient; class QskBoxShapeMetrics; @@ -15,11 +15,11 @@ class QskRectangleNodePrivate; /* QskRectangleNode is for rounded rectangles without a border. - Depending on the type of gradient it uses a different - material/geometry combination. */ -class QSK_EXPORT QskRectangleNode : public QSGGeometryNode +class QSK_EXPORT QskRectangleNode : public QskFillNode { + using Inherited = QskFillNode; + public: QskRectangleNode(); ~QskRectangleNode() override; diff --git a/src/nodes/QskSGNode.cpp b/src/nodes/QskSGNode.cpp index 2b2fddc9..ad26f651 100644 --- a/src/nodes/QskSGNode.cpp +++ b/src/nodes/QskSGNode.cpp @@ -173,9 +173,19 @@ void QskSGNode::resetGeometry( QSGGeometryNode* node ) { if ( auto g = node->geometry() ) { - if ( g->vertexCount() > 0 || g->indexCount() > 0 ) + const bool hasVertices = g->vertexCount() > 0; + const bool hasIndexes = g->indexCount() > 0; + + if ( hasVertices || hasIndexes ) { g->allocate( 0, 0 ); + + if ( hasVertices ) + g->markVertexDataDirty(); + + if ( hasIndexes ) + g->markIndexDataDirty(); + node->markDirty( QSGNode::DirtyGeometry ); } } diff --git a/src/nodes/QskShapeNode.cpp b/src/nodes/QskShapeNode.cpp index 97c3d405..91f9c765 100644 --- a/src/nodes/QskShapeNode.cpp +++ b/src/nodes/QskShapeNode.cpp @@ -4,15 +4,11 @@ *****************************************************************************/ #include "QskShapeNode.h" -#include "QskGradientMaterial.h" #include "QskGradient.h" #include "QskGradientDirection.h" -#include "QskSGNode.h" - -#include +#include "QskFillNodePrivate.h" QSK_QT_PRIVATE_BEGIN -#include #include #include QSK_QT_PRIVATE_END @@ -59,18 +55,9 @@ static void qskUpdateGeometry( const QPainterPath& path, #endif } -class QskShapeNodePrivate final : public QSGGeometryNodePrivate +class QskShapeNodePrivate final : public QskFillNodePrivate { public: - QskShapeNodePrivate() - : geometry( QSGGeometry::defaultAttributes_Point2D(), 0 ) - { - geometry.setDrawingMode( QSGGeometry::DrawTriangles ); - } - - QSGGeometry geometry; - int gradientType = -1; - /* Is there a better way to find out if the path has changed beside storing a copy ( even, when internally with Copy On Write ) ? @@ -80,13 +67,10 @@ class QskShapeNodePrivate final : public QSGGeometryNodePrivate }; QskShapeNode::QskShapeNode() - : QSGGeometryNode( *new QskShapeNodePrivate ) + : QskFillNode( *new QskShapeNodePrivate ) { - Q_D( QskShapeNode ); - - setGeometry( &d->geometry ); - setMaterial( new QSGFlatColorMaterial() ); - setFlag( QSGNode::OwnsMaterial, true ); + setColoring( Monochrome ); + geometry()->setDrawingMode( QSGGeometry::DrawTriangles ); } QskShapeNode::~QskShapeNode() @@ -102,58 +86,21 @@ void QskShapeNode::updateNode( const QPainterPath& path, { d->path = QPainterPath(); d->transform = QTransform(); - QskSGNode::resetGeometry( this ); + resetGeometry(); return; } + setColoring( rect, gradient ); + if ( ( transform != d->transform ) || ( path != d->path ) ) { d->path = path; d->transform = transform; - qskUpdateGeometry( path, transform, d->geometry ); + qskUpdateGeometry( path, transform, *geometry() ); + + geometry()->markVertexDataDirty(); markDirty( QSGNode::DirtyGeometry ); } - - if ( gradient.isMonochrome() ) - { - if ( material() == nullptr || d->gradientType >= 0 ) - { - setMaterial( new QSGFlatColorMaterial() ); - d->gradientType = -1; - } - - const auto color = gradient.startColor().toRgb(); - - /* - We might want to use QSGVertexColorMaterial to improve the "batchability" - as this material does not depend on the specific colors. It could even be - batched with QskBoxNodes, that are usually using QSGVertexColorMaterial as well. - - However we would have to store the color information for each vertex. - For the moment we prefer less memory over better "batchability". - */ - auto mat = static_cast< QSGFlatColorMaterial* >( material() ); - if ( mat->color() != color ) - { - mat->setColor( color ); - markDirty( QSGNode::DirtyMaterial ); - } - } - else - { - const auto effectiveGradient = gradient.effectiveGradient(); - const auto gradientType = effectiveGradient.type(); - - if ( ( material() == nullptr ) || ( gradientType != d->gradientType ) ) - { - setMaterial( QskGradientMaterial::createMaterial( gradientType ) ); - d->gradientType = gradientType; - } - - auto mat = static_cast< QskGradientMaterial* >( material() ); - if ( mat->updateGradient( rect, effectiveGradient ) ) - markDirty( QSGNode::DirtyMaterial ); - } } diff --git a/src/nodes/QskShapeNode.h b/src/nodes/QskShapeNode.h index ca81c116..84779627 100644 --- a/src/nodes/QskShapeNode.h +++ b/src/nodes/QskShapeNode.h @@ -7,7 +7,7 @@ #define QSK_SHAPE_NODE_H #include "QskGlobal.h" -#include +#include "QskFillNode.h" class QskGradient; class QColor; @@ -15,8 +15,10 @@ class QPainterPath; class QskShapeNodePrivate; -class QSK_EXPORT QskShapeNode : public QSGGeometryNode +class QSK_EXPORT QskShapeNode : public QskFillNode { + using Inherited = QskFillNode; + public: QskShapeNode(); ~QskShapeNode() override; diff --git a/src/nodes/QskStrokeNode.cpp b/src/nodes/QskStrokeNode.cpp index b58d5208..29907b8e 100644 --- a/src/nodes/QskStrokeNode.cpp +++ b/src/nodes/QskStrokeNode.cpp @@ -5,145 +5,64 @@ #include "QskStrokeNode.h" #include "QskVertex.h" +#include "QskGradient.h" -#include -#include #include -#include QSK_QT_PRIVATE_BEGIN -#include #include QSK_QT_PRIVATE_END -Q_GLOBAL_STATIC( QSGVertexColorMaterial, qskMaterialColorVertex ) - -static inline void qskMapPolygon( const QPolygonF& polygon, - const QTransform& transform, const QColor& color, QSGGeometry& geometry ) +static inline bool qskIsPenVisible( const QPen& pen ) { - const QskVertex::Color c( color ); + if ( pen.style() == Qt::NoPen ) + return false; - auto p = geometry.vertexDataAsColoredPoint2D(); - - if ( transform.isIdentity() ) + if ( pen.brush().gradient() ) { - for ( int i = 0; i < polygon.count(); i++ ) - { - const auto& pos = polygon[i]; - p[i].set( pos.x(), pos.y(), c.r, c.g, c.b, c.a ); - } + // TODO ... } else { - for ( int i = 0; i < polygon.count(); i++ ) - { - const auto pos = transform.map( polygon[i] ); - p[i].set( pos.x(), pos.y(), c.r, c.g, c.b, c.a ); - } + if ( pen.color().isValid() || ( pen.color().alpha() == 0 ) ) + return false; } + + return true; } -static inline void qskMapPolygon( const QPolygonF& polygon, - const QTransform& transform, QSGGeometry& geometry ) -{ - auto p = geometry.vertexDataAsPoint2D(); - - if ( transform.isIdentity() ) - { - for ( int i = 0; i < polygon.count(); i++ ) - { - const auto& pos = polygon[i]; - p[i].set( pos.x(), pos.y() ); - } - } - else - { - for ( int i = 0; i < polygon.count(); i++ ) - { - const auto pos = transform.map( polygon[i] ); - p[i].set( pos.x(), pos.y() ); - } - } -} - -class QskStrokeNodePrivate final : public QSGGeometryNodePrivate -{ - public: - QskStrokeNodePrivate() - : geometry( QSGGeometry::defaultAttributes_ColoredPoint2D(), 0 ) - { - } - - QSGGeometry geometry; -}; - QskStrokeNode::QskStrokeNode() - : QSGGeometryNode( *new QskStrokeNodePrivate ) { - Q_D( QskStrokeNode ); - - setGeometry( &d->geometry ); - setMaterial( qskMaterialColorVertex ); } QskStrokeNode::~QskStrokeNode() = default; -void QskStrokeNode::setRenderHint( RenderHint renderHint ) -{ - Q_D( QskStrokeNode ); - - const auto material = this->material(); - - if ( renderHint == Colored ) - { - if ( material != qskMaterialColorVertex ) - { - setMaterial( qskMaterialColorVertex ); - delete material; - - const QSGGeometry g( QSGGeometry::defaultAttributes_ColoredPoint2D(), 0 ); - memcpy( ( void* ) &d->geometry, ( void* ) &g, sizeof( QSGGeometry ) ); - } - } - else - { - if ( material == qskMaterialColorVertex ) - { - setMaterial( new QSGFlatColorMaterial() ); - - const QSGGeometry g( QSGGeometry::defaultAttributes_Point2D(), 0 ); - memcpy( ( void* ) &d->geometry, ( void* ) &g, sizeof( QSGGeometry ) ); - } - } -} - -QskStrokeNode::RenderHint QskStrokeNode::renderHint() const -{ - return ( material() == qskMaterialColorVertex ) ? Colored : Flat; -} - void QskStrokeNode::updateNode( const QPainterPath& path, const QPen& pen ) { updateNode( path, QTransform(), pen ); } void QskStrokeNode::updateNode( - const QPainterPath& path, const QTransform& transform, const QPen& pen ) + const QPainterPath& path, const QTransform& transform, const QPen& pen ) { - Q_D( QskStrokeNode ); - - if ( path.isEmpty() || ( pen.style() == Qt::NoPen ) || - !pen.color().isValid() || ( pen.color().alpha() == 0 ) ) + if ( path.isEmpty() || !qskIsPenVisible( pen ) ) { - if ( d->geometry.vertexCount() > 0 ) - { - d->geometry.allocate( 0 ); - markDirty( QSGNode::DirtyGeometry ); - } - + resetGeometry(); return; } + if ( auto qGradient = pen.brush().gradient() ) + { + const auto r = transform.mapRect( path.boundingRect() ); + + QskGradient gradient( *qGradient ); + gradient.setStretchMode( QskGradient::StretchToSize ); + + setColoring( r, gradient ); + } + else + setColoring( pen.color() ); + if ( true ) // For the moment we always update the geometry. TODO ... { /* @@ -176,7 +95,8 @@ void QskStrokeNode::updateNode( constexpr QRectF clipRect; // empty rect: no clipping QDashedStrokeProcessor dashStroker; - dashStroker.process( qtVectorPathForPath( scaledPath ), effectivePen, clipRect, {} ); + dashStroker.process( qtVectorPathForPath( scaledPath ), + effectivePen, clipRect, {} ); const QVectorPath dashedVectorPath( dashStroker.points(), dashStroker.elementCount(), dashStroker.elementTypes(), 0 ); @@ -184,18 +104,20 @@ void QskStrokeNode::updateNode( stroker.process( dashedVectorPath, effectivePen, {}, {} ); } - // 2 vertices for each point - d->geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip ); - d->geometry.allocate( stroker.vertexCount() / 2 ); + auto& geometry = *this->geometry(); - if ( material() == qskMaterialColorVertex ) + // 2 vertices for each point + geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip ); + geometry.allocate( stroker.vertexCount() / 2 ); + + if ( isGeometryColored() ) { const QskVertex::Color c( pen.color() ); const auto v = stroker.vertices(); - auto points = d->geometry.vertexDataAsColoredPoint2D(); + auto points = geometry.vertexDataAsColoredPoint2D(); - for ( int i = 0; i < d->geometry.vertexCount(); i++ ) + for ( int i = 0; i < geometry.vertexCount(); i++ ) { const auto j = 2 * i; points[i].set( v[j], v[j + 1], c.r, c.g, c.b, c.a ); @@ -203,80 +125,11 @@ void QskStrokeNode::updateNode( } else { - memcpy( d->geometry.vertexData(), stroker.vertices(), + memcpy( geometry.vertexData(), stroker.vertices(), stroker.vertexCount() * sizeof( float ) ); } + geometry.markVertexDataDirty(); markDirty( QSGNode::DirtyGeometry ); } - - if ( material() != qskMaterialColorVertex ) - { - const auto color = pen.color().toRgb(); - - auto flatMaterial = static_cast< QSGFlatColorMaterial* >( material() ); - if ( flatMaterial->color() != color ) - { - flatMaterial->setColor( color ); - markDirty( QSGNode::DirtyMaterial ); - } - } -} - -void QskStrokeNode::updateNode( const QPolygonF& polygon, - qreal lineWidth, const QColor& color ) -{ - updateNode( polygon, QTransform(), lineWidth, color ); -} - -/* - For polygons with a small lineWidth ( < 2 ) or a line without - connections we might get away with a simple and fast implementation - using DrawLineStrip/DrawLineLoop - */ -void QskStrokeNode::updateNode( const QPolygonF& polygon, - const QTransform& transform, qreal lineWidth, const QColor& color ) -{ - Q_D( QskStrokeNode ); - - if ( polygon.isEmpty() || !color.isValid() || ( color.alpha() == 0 ) ) - { - if ( d->geometry.vertexCount() > 0 ) - { - d->geometry.allocate( 0 ); - markDirty( QSGNode::DirtyGeometry ); - } - - return; - } - - d->geometry.setDrawingMode( QSGGeometry::DrawLineStrip ); - - const float lineWidthF = lineWidth; - - if( lineWidthF != d->geometry.lineWidth() ) - d->geometry.setLineWidth( lineWidthF ); - - if ( true ) // TODO - { - d->geometry.allocate( polygon.count() ); - - if ( material() == qskMaterialColorVertex ) - { - qskMapPolygon( polygon, transform, color, d->geometry ); - markDirty( QSGNode::DirtyGeometry ); - } - else - { - qskMapPolygon( polygon, transform, d->geometry ); - markDirty( QSGNode::DirtyGeometry ); - - auto flatMaterial = static_cast< QSGFlatColorMaterial* >( material() ); - if ( flatMaterial->color() != color ) - { - flatMaterial->setColor( color ); - markDirty( QSGNode::DirtyMaterial ); - } - } - } } diff --git a/src/nodes/QskStrokeNode.h b/src/nodes/QskStrokeNode.h index 31e8a446..01abaaba 100644 --- a/src/nodes/QskStrokeNode.h +++ b/src/nodes/QskStrokeNode.h @@ -7,44 +7,26 @@ #define QSK_STROKE_NODE_H #include "QskGlobal.h" -#include +#include "QskFillNode.h" class QPen; class QPainterPath; class QPolygonF; -class QskStrokeNodePrivate; - -class QSK_EXPORT QskStrokeNode : public QSGGeometryNode +class QSK_EXPORT QskStrokeNode : public QskFillNode { + using Inherited = QskFillNode; + public: QskStrokeNode(); ~QskStrokeNode() override; - /* - We only support monochrome pens ( QPen::color() ) and using a - QSGFlatColorMaterial is good enough. However QSGVertexColorMaterial - usually allows better batching. So Flat vs. Colored is a tradeoff - between memory and performance. - */ - enum RenderHint - { - Flat, - Colored - }; - - void setRenderHint( RenderHint ); - RenderHint renderHint() const; - void updateNode( const QPainterPath&, const QPen& ); void updateNode( const QPainterPath&, const QTransform&, const QPen& ); - void updateNode( const QPolygonF&, qreal lineWidth, const QColor& ); - void updateNode( const QPolygonF&, const QTransform&, + void updateNode0( const QPolygonF&, qreal lineWidth, const QColor& ); + void updateNode0( const QPolygonF&, const QTransform&, qreal lineWidth, const QColor& ); - - private: - Q_DECLARE_PRIVATE( QskStrokeNode ) }; #endif diff --git a/src/nodes/QskTickmarksNode.cpp b/src/nodes/QskTickmarksNode.cpp index 1753570d..11604d2c 100644 --- a/src/nodes/QskTickmarksNode.cpp +++ b/src/nodes/QskTickmarksNode.cpp @@ -22,7 +22,6 @@ class QskTickmarksNodePrivate final : public QSGGeometryNodePrivate : geometry( QSGGeometry::defaultAttributes_Point2D(), 0 ) { geometry.setDrawingMode( QSGGeometry::DrawLines ); - geometry.setVertexDataPattern( QSGGeometry::StaticPattern ); } QSGGeometry geometry;