diff --git a/designsystems/material3/QskMaterial3SliderSkinlet.cpp b/designsystems/material3/QskMaterial3SliderSkinlet.cpp index 2befad91..f87bf94c 100644 --- a/designsystems/material3/QskMaterial3SliderSkinlet.cpp +++ b/designsystems/material3/QskMaterial3SliderSkinlet.cpp @@ -6,7 +6,7 @@ #include "QskMaterial3SliderSkinlet.h" #include -#include +#include #include #include @@ -43,45 +43,6 @@ static inline qreal qskTickValue( const QskSlider* slider, int index ) return slider->maximum(); } -namespace -{ - class ClipNode : public QSGClipNode - { - public: - ClipNode() - : m_geometry( QSGGeometry::defaultAttributes_Point2D(), 0 ) - { - m_geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip ); - setGeometry( &m_geometry ); - } - - void setRegion( const QRectF& boundingRect, const QRectF& rect ) - { - if ( ( rect == clipRect() ) && ( m_boundingRect == boundingRect ) ) - return; - - setIsRectangular( false ); - setClipRect( rect ); - - m_geometry.allocate( 2 * 5 ); // 2 points per line - - const auto l = reinterpret_cast< QskVertex::Line* >( m_geometry.vertexData() ); - l[0].setLine( boundingRect.topLeft(), rect.topLeft() ); - l[1].setLine( boundingRect.topRight(), rect.topRight() ); - l[2].setLine( boundingRect.bottomRight(), rect.bottomRight() ); - l[3].setLine( boundingRect.bottomLeft(), rect.bottomLeft() ); - l[4] = l[0]; - - m_geometry.markVertexDataDirty(); - markDirty( QSGNode::DirtyGeometry ); - } - - private: - QRectF m_boundingRect; - QSGGeometry m_geometry; - }; -} - QskMaterial3SliderSkinlet::QskMaterial3SliderSkinlet( QskSkin* skin ) : Inherited( skin ) { @@ -128,12 +89,10 @@ QSGNode* QskMaterial3SliderSkinlet::updateSubNode( { const auto slider = static_cast< const QskSlider* >( skinnable ); - auto clipNode = QskSGNode::ensureNode< ClipNode >( node ); + auto clipNode = QskSGNode::ensureNode< QskClipNode >( node ); - clipNode->setRegion( - slider->subControlRect( Q::Panel ), - slider->subControlRect( Q::Handle ) - ); + clipNode->setRegion( slider->subControlRect( Q::Panel ), + slider->subControlRect( Q::Handle ) ); QskSGNode::setNodeRole( clippedNode, nodeRole ); QskSGNode::setParentNode( clippedNode, clipNode ); diff --git a/playground/plots/QskPlotView.cpp b/playground/plots/QskPlotView.cpp index 18fc60e0..8c346f37 100644 --- a/playground/plots/QskPlotView.cpp +++ b/playground/plots/QskPlotView.cpp @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include @@ -315,7 +315,7 @@ void QskPlotView::updateNode( QSGNode* node ) if ( m_data->needsClipping() ) { if ( itemsNode == nullptr || itemsNode->type() != QSGNode::ClipNodeType ) - itemsNode = new QskBoxClipNode(); + itemsNode = new QskClipNode(); } else { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0c8fa825..cd594404 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -105,13 +105,13 @@ list(APPEND HEADERS nodes/QskArcRenderNode.h nodes/QskBasicLinesNode.h nodes/QskBoxNode.h - nodes/QskBoxClipNode.h nodes/QskBoxRectangleNode.h nodes/QskBoxRenderer.h nodes/QskBoxMetrics.h nodes/QskBoxBasicStroker.h nodes/QskBoxGradientStroker.h nodes/QskBoxShadowNode.h + nodes/QskClipNode.h nodes/QskColorRamp.h nodes/QskFillNode.h nodes/QskGraduationNode.h @@ -145,13 +145,13 @@ list(APPEND SOURCES nodes/QskArcRenderNode.cpp nodes/QskBasicLinesNode.cpp nodes/QskBoxNode.cpp - nodes/QskBoxClipNode.cpp nodes/QskBoxRectangleNode.cpp nodes/QskBoxRenderer.cpp nodes/QskBoxMetrics.cpp nodes/QskBoxBasicStroker.cpp nodes/QskBoxGradientStroker.cpp nodes/QskBoxShadowNode.cpp + nodes/QskClipNode.cpp nodes/QskColorRamp.cpp nodes/QskFillNode.cpp nodes/QskGraduationNode.cpp diff --git a/src/controls/QskSkinlet.cpp b/src/controls/QskSkinlet.cpp index 33a9661f..b7add39f 100644 --- a/src/controls/QskSkinlet.cpp +++ b/src/controls/QskSkinlet.cpp @@ -11,10 +11,10 @@ #include "QskBoxBorderColors.h" #include "QskBoxBorderMetrics.h" #include "QskBoxNode.h" -#include "QskBoxClipNode.h" #include "QskBoxRectangleNode.h" #include "QskBoxShapeMetrics.h" #include "QskBoxHints.h" +#include "QskClipNode.h" #include "QskColorFilter.h" #include "QskControl.h" #include "QskFunctions.h" @@ -641,15 +641,14 @@ QSGNode* QskSkinlet::updateBoxClipNode( const QskSkinnable* skinnable, QSGNode* QskSkinlet::updateBoxClipNode( const QskSkinnable* skinnable, QSGNode* node, const QRectF& rect, QskAspect::Subcontrol subControl ) { - auto clipNode = QskSGNode::ensureNode< QskBoxClipNode >( node ); + auto clipNode = QskSGNode::ensureNode< QskClipNode >( node ); const auto margins = skinnable->marginHint( subControl ); const auto clipRect = rect.marginsRemoved( margins ); if ( clipRect.isEmpty() ) { - clipNode->setIsRectangular( true ); - clipNode->setClipRect( clipRect ); + clipNode->setRect( clipRect ); } else { diff --git a/src/nodes/QskBoxClipNode.cpp b/src/nodes/QskBoxClipNode.cpp deleted file mode 100644 index 1006cb81..00000000 --- a/src/nodes/QskBoxClipNode.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) The authors - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#include "QskBoxClipNode.h" -#include "QskBoxBorderMetrics.h" -#include "QskBoxRenderer.h" -#include "QskBoxShapeMetrics.h" -#include "QskFunctions.h" - -#include - -static inline QskHashValue qskMetricsHash( - const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& border ) -{ - QskHashValue hash = 13000; - - hash = shape.hash( hash ); - return border.hash( hash ); -} - -QskBoxClipNode::QskBoxClipNode() - : m_hash( 0 ) - , m_geometry( QSGGeometry::defaultAttributes_Point2D(), 0 ) -{ - setGeometry( &m_geometry ); -} - -QskBoxClipNode::~QskBoxClipNode() -{ -} - -void QskBoxClipNode::setBox( const QQuickWindow* window, const QRectF& rect, - const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& border ) -{ - const auto hash = qskMetricsHash( shape, border ); - if ( hash == m_hash && rect == m_rect ) - return; - - m_rect = rect; - m_hash = hash; - - bool isRectangular = false; - -#if 0 - /* - Depending on isRectangular the "renderer can use scissoring instead of stencil, - which is significantly faster." - - However the batch renderer ( qsgbatchrenderer.cpp ) is rounding the clip rectangle - to integers and the clip might become too small/large. - - So we always have to use stencil clipping - even if it might have a negative - impact on the performance. TODO ... - */ - - if ( shape.isRectangle() ) - isRectangular = true; -#endif - - if ( isRectangular ) - { - if ( m_geometry.vertexCount() > 0 ) - m_geometry.allocate( 0 ); - - setIsRectangular( true ); - } - else - { - setIsRectangular( false ); - - QskBoxRenderer renderer( window ); - renderer.setFillLines( rect, shape, border, m_geometry ); - } - - /* - Even in situations, where the clipping is not rectangular, it is - useful to know its bounding rectangle - */ - setClipRect( qskValidOrEmptyInnerRect( rect, border.widths() ) ); - - m_geometry.markVertexDataDirty(); - markDirty( QSGNode::DirtyGeometry ); -} diff --git a/src/nodes/QskClipNode.cpp b/src/nodes/QskClipNode.cpp new file mode 100644 index 00000000..dfc5fe6f --- /dev/null +++ b/src/nodes/QskClipNode.cpp @@ -0,0 +1,151 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskClipNode.h" +#include "QskBoxBorderMetrics.h" +#include "QskBoxRenderer.h" +#include "QskBoxShapeMetrics.h" +#include "QskFunctions.h" +#include "QskVertex.h" + +#include + +static inline QskHashValue qskMetricsHash( + const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& border ) +{ + QskHashValue hash = 13000; + + hash = shape.hash( hash ); + return border.hash( hash ); +} + +static inline void qskSetBoundingRect( QSGClipNode* node, const QRectF& rect ) +{ + /* + Depending on isRectangular: the "scene graph renderer can use + scissoring instead of stencil, which is significantly faster." + + However the batch renderer ( qsgbatchrenderer.cpp ) is rounding + the clip rectangle to integers and the clip might become too small/large. + So we always have to use stencil clipping - even if it might have a negative + impact on the performance. + + When isRectangular is set to false the clipRect is not used from the + renderer and we use the memory for the storing the bounding rectangle. + */ + + node->setIsRectangular( false ); + node->setClipRect( rect ); +} + +static inline QskVertex::Line* qskAllocateLines( + QSGGeometry& geometry, int lineCount ) +{ + geometry.allocate( 2 * lineCount ); // 2 points per line + return reinterpret_cast< QskVertex::Line* >( geometry.vertexData() ); +} + +QskClipNode::QskClipNode() + : m_hash( 0 ) + , m_geometry( QSGGeometry::defaultAttributes_Point2D(), 0 ) +{ + setGeometry( &m_geometry ); +} + +QskClipNode::~QskClipNode() +{ +} + +void QskClipNode::setRect( const QRectF& rect ) +{ + setRegion( rect, QRectF() ); +} + +void QskClipNode::setRegion( const QRectF& rect, const QRectF& excludedRect ) +{ + if ( rect.isEmpty() ) + { + /* + what about rectangles having a width/height + of 0 ( f.e lines ) TODO ... + */ + reset(); + return; + } + + const auto innerRect = excludedRect.isEmpty() + ? QRectF() : excludedRect.intersected( rect ); + + const auto hash = qHashBits( &innerRect, sizeof( innerRect ), 1450 ); + if ( ( hash == m_hash ) && ( rect == Inherited::clipRect() ) ) + return; + + qskSetBoundingRect( this, rect ); + m_hash = hash; + + m_geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip ); + + if ( innerRect.isEmpty() ) + { + const auto l = qskAllocateLines( m_geometry, 2 ); + + l[0].setLine( rect.topLeft(), rect.topRight() ); + l[1].setLine( rect.bottomLeft(), rect.bottomRight() ); + } + else + { + const auto l = qskAllocateLines( m_geometry, 5 ); + + l[0].setLine( rect.topLeft(), innerRect.topLeft() ); + l[1].setLine( rect.topRight(), innerRect.topRight() ); + l[2].setLine( rect.bottomRight(), innerRect.bottomRight() ); + l[3].setLine( rect.bottomLeft(), innerRect.bottomLeft() ); + l[4] = l[0]; + } + + m_geometry.markVertexDataDirty(); + markDirty( QSGNode::DirtyGeometry ); +} + +void QskClipNode::setBox( const QQuickWindow* window, const QRectF& rect, + const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& border ) +{ + if ( rect.isEmpty() ) + { + reset(); + return; + } + + const auto hash = qskMetricsHash( shape, border ); + if ( hash == m_hash && rect == boundingRectangle() ) + return; + + qskSetBoundingRect( this, rect ); + m_hash = hash; + + QskBoxRenderer renderer( window ); + renderer.setFillLines( rect, shape, border, m_geometry ); + + m_geometry.markVertexDataDirty(); + markDirty( QSGNode::DirtyGeometry ); +} + +void QskClipNode::reset() +{ + Inherited::setIsRectangular( true ); + Inherited::setClipRect( QRectF() ); + + if ( m_geometry.vertexData() ) + { + m_geometry.allocate( 0 ); + m_geometry.markVertexDataDirty(); + } + + if ( m_hash != 0 ) + { + m_hash = 0; + markDirty( QSGNode::DirtyGeometry ); + } +} diff --git a/src/nodes/QskBoxClipNode.h b/src/nodes/QskClipNode.h similarity index 50% rename from src/nodes/QskBoxClipNode.h rename to src/nodes/QskClipNode.h index 1a637792..b8e49273 100644 --- a/src/nodes/QskBoxClipNode.h +++ b/src/nodes/QskClipNode.h @@ -3,8 +3,8 @@ * SPDX-License-Identifier: BSD-3-Clause *****************************************************************************/ -#ifndef QSK_BOX_CLIP_NODE_H -#define QSK_BOX_CLIP_NODE_H +#ifndef QSK_CLIP_NODE_H +#define QSK_CLIP_NODE_H #include "QskGlobal.h" #include @@ -14,20 +14,36 @@ class QskBoxBorderMetrics; class QQuickWindow; -class QSK_EXPORT QskBoxClipNode : public QSGClipNode +class QSK_EXPORT QskClipNode : public QSGClipNode { + using Inherited = QSGClipNode; + public: - QskBoxClipNode(); - ~QskBoxClipNode() override; + QskClipNode(); + ~QskClipNode() override; + + void setRect( const QRectF& ); + void setRegion( const QRectF&, const QRectF& excludedRect ); void setBox( const QQuickWindow*, const QRectF&, const QskBoxShapeMetrics&, const QskBoxBorderMetrics& ); - private: - QskHashValue m_hash; - QRectF m_rect; + QRectF boundingRectangle() const; + private: + void reset(); + + void setIsRectangular( bool ) = delete; + void setClipRect( const QRectF& ) = delete; + QRectF clipRect() const = delete; + + QskHashValue m_hash; QSGGeometry m_geometry; }; +inline QRectF QskClipNode::boundingRectangle() const +{ + return Inherited::clipRect(); +} + #endif