From d84e75a3d1bd8cb1e8733d7ead5b7c21dfc99345 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Thu, 25 May 2023 14:52:54 +0200 Subject: [PATCH] implementation of QskStippleMetrics completed --- src/CMakeLists.txt | 2 + src/common/QskStippleMetrics.cpp | 70 ++++++++++- src/common/QskStippleMetrics.h | 8 ++ src/controls/QskSkinHintTableEditor.cpp | 39 +++++- src/controls/QskSkinHintTableEditor.h | 17 +++ src/controls/QskSkinnable.cpp | 19 +++ src/controls/QskSkinnable.h | 5 + src/controls/QskVariantAnimator.cpp | 2 + src/nodes/QskStippledLineRenderer.cpp | 154 ++++++++++++++++++++++++ src/nodes/QskStippledLineRenderer.h | 47 ++++++++ 10 files changed, 358 insertions(+), 5 deletions(-) create mode 100644 src/nodes/QskStippledLineRenderer.cpp create mode 100644 src/nodes/QskStippledLineRenderer.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e9e18947..07b1b761 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -116,6 +116,7 @@ list(APPEND HEADERS nodes/QskScaleRenderer.h nodes/QskSGNode.h nodes/QskStrokeNode.h + nodes/QskStippledLineRenderer.h nodes/QskShapeNode.h nodes/QskGradientMaterial.h nodes/QskTextNode.h @@ -146,6 +147,7 @@ list(APPEND SOURCES nodes/QskScaleRenderer.cpp nodes/QskSGNode.cpp nodes/QskStrokeNode.cpp + nodes/QskStippledLineRenderer.cpp nodes/QskShapeNode.cpp nodes/QskGradientMaterial.cpp nodes/QskTextNode.cpp diff --git a/src/common/QskStippleMetrics.cpp b/src/common/QskStippleMetrics.cpp index 7bee7e54..6b4d0520 100644 --- a/src/common/QskStippleMetrics.cpp +++ b/src/common/QskStippleMetrics.cpp @@ -16,11 +16,19 @@ static void qskRegisterStippleMetrics() #if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) QMetaType::registerEqualsComparator< QskStippleMetrics >(); #endif + + QMetaType::registerConverter< QPen, QskStippleMetrics >( + []( const QPen& pen ) { return QskStippleMetrics( pen ); } ); + + QMetaType::registerConverter< Qt::PenStyle, QskStippleMetrics >( + []( Qt::PenStyle style ) { return QskStippleMetrics( style ); } ); } -static inline QVector< qreal > qskDashPattern( const Qt::PenStyle& style ) +Q_CONSTRUCTOR_FUNCTION( qskRegisterStippleMetrics ) + +QVector< qreal > qskDashPattern( Qt::PenStyle style ) { - static QVector< qreal > pattern[] = + static const QVector< qreal > pattern[] = { {}, { 1 }, { 4, 2 }, { 1, 2 }, { 4, 2, 1, 2 }, { 4, 2, 1, 2, 1, 2 }, {} @@ -29,7 +37,22 @@ static inline QVector< qreal > qskDashPattern( const Qt::PenStyle& style ) return pattern[ style ]; } -Q_CONSTRUCTOR_FUNCTION( qskRegisterStippleMetrics ) +static inline qreal qskInterpolated( qreal from, qreal to, qreal ratio ) +{ + return from + ( to - from ) * ratio; +} + +static inline QVector< qreal > qskInterpolatedSpaces( + const QVector< qreal >& pattern, qreal progress ) +{ + QVector< qreal > interpolated; + interpolated.reserve( pattern.count() ); + + for ( int i = 1; i < pattern.count(); i += 2 ) + interpolated[i] = progress * pattern[i]; + + return interpolated; +} QskStippleMetrics::QskStippleMetrics( Qt::PenStyle penStyle ) : m_pattern( qskDashPattern( penStyle ) ) @@ -56,6 +79,47 @@ void QskStippleMetrics::setOffset( qreal offset ) noexcept m_offset = offset; } +QskStippleMetrics QskStippleMetrics::interpolated( + const QskStippleMetrics& to, qreal progress ) const +{ + if ( *this == to ) + return to; + + const auto offset = qskInterpolated( m_offset, to.m_offset, progress ); + + QVector< qreal > pattern; + + if ( isSolid() ) + { + pattern = qskInterpolatedSpaces( to.m_pattern, progress ); + } + else if ( to.isSolid() ) + { + pattern = qskInterpolatedSpaces( m_pattern, 1.0 - progress ); + } + else + { + const auto count = qMax( m_pattern.count(), to.m_pattern.count() ); + pattern.reserve( count ); + + for ( int i = 0; i < count; i++ ) + { + const auto v1 = m_pattern.value( i, 0.0 ); + const auto v2 = to.m_pattern.value( i, 0.0 ); + + pattern += qskInterpolated( v1, v2, progress ); + } + } + + return QskStippleMetrics( pattern, offset ); +} + +QVariant QskStippleMetrics::interpolate( + const QskStippleMetrics& from, const QskStippleMetrics& to, qreal progress ) +{ + return QVariant::fromValue( from.interpolated( to, progress ) ); +} + QskHashValue QskStippleMetrics::hash( QskHashValue seed ) const noexcept { auto hash = qHash( m_offset, seed ); diff --git a/src/common/QskStippleMetrics.h b/src/common/QskStippleMetrics.h index 5029b0d9..a637faf1 100644 --- a/src/common/QskStippleMetrics.h +++ b/src/common/QskStippleMetrics.h @@ -38,6 +38,12 @@ class QSK_EXPORT QskStippleMetrics void setPattern( const QVector< qreal >& ); QVector< qreal > pattern() const; + QskStippleMetrics interpolated( + const QskStippleMetrics&, qreal value ) const; + + static QVariant interpolate( const QskStippleMetrics&, + const QskStippleMetrics&, qreal progress ); + QskHashValue hash( QskHashValue seed = 0 ) const noexcept; private: @@ -85,6 +91,8 @@ inline bool QskStippleMetrics::isSolid() const noexcept return m_pattern.count() == 1; } +QSK_EXPORT QVector< qreal > qskDashPattern( Qt::PenStyle ); + #ifndef QT_NO_DEBUG_STREAM class QDebug; diff --git a/src/controls/QskSkinHintTableEditor.cpp b/src/controls/QskSkinHintTableEditor.cpp index 99f402ea..7e5e9199 100644 --- a/src/controls/QskSkinHintTableEditor.cpp +++ b/src/controls/QskSkinHintTableEditor.cpp @@ -12,6 +12,7 @@ #include "QskBoxBorderMetrics.h" #include "QskBoxBorderColors.h" #include "QskShadowMetrics.h" +#include "QskStippleMetrics.h" #include "QskGraphic.h" namespace @@ -130,6 +131,11 @@ namespace { return aspect | QskAspect::Symbol; } + + inline QskAspect aspectStipple( QskAspect aspect ) + { + return aspect | QskAspect::Style; + } } QskSkinHintTableEditor::QskSkinHintTableEditor( QskSkinHintTable* table ) @@ -583,8 +589,8 @@ void QskSkinHintTableEditor::setArcMetrics( QskAspect aspect, setMetricHint( aspectShape( aspect ), arcMetrics, combination ); } -bool QskSkinHintTableEditor::removeArcMetrics( QskAspect aspect, - QskStateCombination combination ) +bool QskSkinHintTableEditor::removeArcMetrics( + QskAspect aspect, QskStateCombination combination ) { return removeMetricHint( aspectShape( aspect ), combination ); } @@ -594,6 +600,35 @@ QskArcMetrics QskSkinHintTableEditor::arcMetrics( QskAspect aspect ) const return metricHint< QskArcMetrics >( aspectShape( aspect ) ); } +void QskSkinHintTableEditor::setStippleMetrics( QskAspect aspect, + Qt::PenStyle penStyle, QskStateCombination combination ) +{ + setStippleMetrics( aspect, QskStippleMetrics( penStyle ), combination ); +} + +void QskSkinHintTableEditor::setStippleMetrics( QskAspect aspect, + const QVector< qreal >& dashPattern, QskStateCombination combination ) +{ + setStippleMetrics( aspect, QskStippleMetrics( dashPattern ), combination ); +} + +void QskSkinHintTableEditor::setStippleMetrics( QskAspect aspect, + const QskStippleMetrics& metrics, QskStateCombination combination ) +{ + setMetricHint( aspectStipple( aspect ), metrics, combination ); +} + +bool QskSkinHintTableEditor::removeStippleMetrics( + QskAspect aspect, QskStateCombination combination ) +{ + return removeMetricHint( aspectStipple( aspect ), combination ); +} + +QskStippleMetrics QskSkinHintTableEditor::stippleMetrics( QskAspect aspect ) const +{ + return metricHint< QskStippleMetrics >( aspectStipple( aspect ) ); +} + void QskSkinHintTableEditor::setTextOptions( QskAspect aspect, Qt::TextElideMode elideMode, QskTextOptions::WrapMode wrapMode, QskStateCombination combination ) diff --git a/src/controls/QskSkinHintTableEditor.h b/src/controls/QskSkinHintTableEditor.h index 10565b81..db3e7f6c 100644 --- a/src/controls/QskSkinHintTableEditor.h +++ b/src/controls/QskSkinHintTableEditor.h @@ -14,6 +14,7 @@ #include #include +#include class QskArcMetrics; class QskMargins; @@ -22,6 +23,7 @@ class QskBoxShapeMetrics; class QskBoxBorderMetrics; class QskBoxBorderColors; class QskShadowMetrics; +class QskStippleMetrics; class QskGraphic; class QSK_EXPORT QskSkinHintTableEditor @@ -265,6 +267,21 @@ class QSK_EXPORT QskSkinHintTableEditor QskArcMetrics arcMetrics( QskAspect ) const; + // lines + + void setStippleMetrics( QskAspect, Qt::PenStyle, + QskStateCombination = QskStateCombination() ); + + void setStippleMetrics( QskAspect, const QVector< qreal >&, + QskStateCombination = QskStateCombination() ); + + void setStippleMetrics( QskAspect, const QskStippleMetrics&, + QskStateCombination = QskStateCombination() ); + + bool removeStippleMetrics( QskAspect, QskStateCombination = QskStateCombination() ); + + QskStippleMetrics stippleMetrics( QskAspect ) const; + // text options flag void setTextOptions( QskAspect, diff --git a/src/controls/QskSkinnable.cpp b/src/controls/QskSkinnable.cpp index f3d273cb..5b6421b4 100644 --- a/src/controls/QskSkinnable.cpp +++ b/src/controls/QskSkinnable.cpp @@ -23,6 +23,7 @@ #include "QskBoxBorderMetrics.h" #include "QskBoxBorderColors.h" #include "QskShadowMetrics.h" +#include "QskStippleMetrics.h" #include "QskBoxHints.h" #include "QskGradient.h" #include "QskTextOptions.h" @@ -638,6 +639,24 @@ QskArcMetrics QskSkinnable::arcMetricsHint( this, aspect | QskAspect::Shape, status ); } +bool QskSkinnable::setStippleMetricsHint( + QskAspect aspect, const QskStippleMetrics& metrics ) +{ + return qskSetMetric( this, aspect | QskAspect::Style, metrics ); +} + +bool QskSkinnable::resetStippleMetricsHint( QskAspect aspect ) +{ + return resetMetric( aspect | QskAspect::Style ); +} + +QskStippleMetrics QskSkinnable::stippleMetricsHint( + QskAspect aspect, QskSkinHintStatus* status ) const +{ + return qskMetric< QskStippleMetrics >( + this, aspect | QskAspect::Style, status ); +} + bool QskSkinnable::setSpacingHint( const QskAspect aspect, qreal spacing ) { return qskSetMetric( this, aspect | QskAspect::Spacing, spacing ); diff --git a/src/controls/QskSkinnable.h b/src/controls/QskSkinnable.h index 3a736a83..2f28091c 100644 --- a/src/controls/QskSkinnable.h +++ b/src/controls/QskSkinnable.h @@ -30,6 +30,7 @@ class QskBoxShapeMetrics; class QskBoxBorderMetrics; class QskBoxBorderColors; class QskShadowMetrics; +class QskStippleMetrics; class QskTextOptions; class QskBoxHints; class QskGradient; @@ -228,6 +229,10 @@ class QSK_EXPORT QskSkinnable bool resetArcMetricsHint( QskAspect ); QskArcMetrics arcMetricsHint( QskAspect, QskSkinHintStatus* = nullptr ) const; + bool setStippleMetricsHint( QskAspect, const QskStippleMetrics& ); + bool resetStippleMetricsHint( QskAspect ); + QskStippleMetrics stippleMetricsHint( QskAspect, QskSkinHintStatus* = nullptr ) const; + bool setSpacingHint( QskAspect, qreal ); bool resetSpacingHint( QskAspect ); qreal spacingHint( QskAspect, QskSkinHintStatus* = nullptr ) const; diff --git a/src/controls/QskVariantAnimator.cpp b/src/controls/QskVariantAnimator.cpp index 2c7c54df..dc196772 100644 --- a/src/controls/QskVariantAnimator.cpp +++ b/src/controls/QskVariantAnimator.cpp @@ -9,6 +9,7 @@ #include "QskBoxBorderMetrics.h" #include "QskBoxShapeMetrics.h" #include "QskShadowMetrics.h" +#include "QskStippleMetrics.h" #include "QskColorFilter.h" #include "QskGradient.h" #include "QskMargins.h" @@ -46,6 +47,7 @@ static void qskRegisterInterpolator() qRegisterAnimationInterpolator< QskBoxBorderColors >( QskBoxBorderColors::interpolate ); qRegisterAnimationInterpolator< QskTextColors >( QskTextColors::interpolate ); qRegisterAnimationInterpolator< QskShadowMetrics >( QskShadowMetrics::interpolate ); + qRegisterAnimationInterpolator< QskStippleMetrics >( QskStippleMetrics::interpolate ); qRegisterAnimationInterpolator< QskArcMetrics >( QskArcMetrics::interpolate ); } diff --git a/src/nodes/QskStippledLineRenderer.cpp b/src/nodes/QskStippledLineRenderer.cpp new file mode 100644 index 00000000..f578f478 --- /dev/null +++ b/src/nodes/QskStippledLineRenderer.cpp @@ -0,0 +1,154 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskStippledLineRenderer.h" + +QSK_QT_PRIVATE_BEGIN +#include +QSK_QT_PRIVATE_END + +namespace +{ + /* + Thanks to the hooks of the stroker classes we can make use + of QDashStroker without having to deal with the overhead of + QPainterPaths. But it might be worth to check if this could + be done in a shader. TODO ... + */ + class DashStroker : public QDashStroker + { + public: + DashStroker( QskStippledLineRenderer* renderer ) + : QDashStroker( nullptr ) + , m_renderer( renderer ) + { + setDashOffset( renderer->metrics().offset() ); + setDashPattern( renderer->metrics().pattern() ); + + m_elements.reserve( 2 ); + } + + void renderDashes( qreal x1, qreal y1, qreal x2, qreal y2 ) + { + if ( ( x1 == x2 ) && ( y1 == y2 ) ) + return; + + setMoveToHook( moveTo ); + setLineToHook( lineTo ); + + begin( this ); + + m_elements.add( { QPainterPath::MoveToElement, x1, y1 } ); + m_elements.add( { QPainterPath::LineToElement, x2, y2 } ); + + processCurrentSubpath(); + + end(); + } + + qsizetype dashCount( qreal x1, qreal y1, qreal x2, qreal y2 ) + { + if ( ( x1 == x2 ) && ( y1 == y2 ) ) + return 0; + + /* + There should be a faster way to calculate the + number of points. TODO ... + */ + setMoveToHook( countMoveTo ); + setLineToHook( countLineTo ); + + m_count = 0; + + begin( this ); + + m_elements.add( { QPainterPath::MoveToElement, x1, y1 } ); + m_elements.add( { QPainterPath::LineToElement, x2, y2 } ); + + processCurrentSubpath(); + + end(); + + return m_count; + } + + private: + static void moveTo( qfixed x, qfixed y, void* data ) + { + auto stroker = reinterpret_cast< DashStroker* >( data ); + + stroker->m_x = x; + stroker->m_y = y; + } + + static void lineTo( qfixed x, qfixed y, void* data ) + { + auto stroker = reinterpret_cast< DashStroker* >( data ); + stroker->m_renderer->renderDash( stroker->m_x, stroker->m_y, x, y ); + } + + static void countMoveTo( qfixed, qfixed, void* ) + { + } + + static void countLineTo( qfixed, qfixed, void* data ) + { + auto stroker = reinterpret_cast< DashStroker* >( data ); + stroker->m_count++; + } + + QskStippledLineRenderer* m_renderer; + + qsizetype m_count = 0; + qreal m_x, m_y; + }; +} + +QskStippledLineRenderer::QskStippledLineRenderer( const QskStippleMetrics& metrics ) + : m_metrics( metrics ) +{ +} + +QskStippledLineRenderer::~QskStippledLineRenderer() +{ +} + +qsizetype QskStippledLineRenderer::dashCount( + const QPointF& p1, const QPointF& p2 ) const +{ + return dashCount( p1.x(), p1.y(), p2.x(), p2.y() ); +} + +qsizetype QskStippledLineRenderer::dashCount( const QLineF& line ) const +{ + return dashCount( line.x1(), line.y1(), line.x2(), line.y2() ); +} + +qsizetype QskStippledLineRenderer::dashCount( + qreal x1, qreal y1, qreal x2, qreal y2 ) const +{ + auto that = const_cast< QskStippledLineRenderer* >( this ); + return DashStroker( that ).dashCount( x1, y1, x2, y2 ); +} + +void QskStippledLineRenderer::renderLine( const QPointF& p1, const QPointF& p2 ) +{ + renderLine( p1.x(), p1.y(), p2.x(), p2.y() ); +} + +void QskStippledLineRenderer::renderLine( const QLineF& line ) +{ + renderLine( line.x1(), line.y1(), line.x2(), line.y2() ); +} + +void QskStippledLineRenderer::renderLine( qreal x1, qreal y1, qreal x2, qreal y2 ) +{ + DashStroker( this ).renderDashes( x1, y1, x2, y2 ); +} + +void QskStippledLineRenderer::renderDash( qreal, qreal, qreal, qreal ) +{ + // nop +} diff --git a/src/nodes/QskStippledLineRenderer.h b/src/nodes/QskStippledLineRenderer.h new file mode 100644 index 00000000..acda0bb7 --- /dev/null +++ b/src/nodes/QskStippledLineRenderer.h @@ -0,0 +1,47 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_STIPPLED_LINE_RENDERER_H +#define QSK_STIPPLED_LINE_RENDERER_H + +#include "QskStippleMetrics.h" + +class QLineF; +class QPointF; + +/* + A wrapper for the non public QDashStroker class, tailored for + splitting lines into dashes/dots. It is faster than QPainterPathStroker + ( no QPainterPath involved ), but supports simple lines only. + */ +class QskStippledLineRenderer +{ + public: + QskStippledLineRenderer( const QskStippleMetrics& ); + virtual ~QskStippledLineRenderer(); + + qsizetype dashCount( qreal x1, qreal y1, qreal x2, qreal y2 ) const; + qsizetype dashCount( const QPointF&, const QPointF& ) const; + qsizetype dashCount( const QLineF& ) const; + + void renderLine( qreal x1, qreal y1, qreal x2, qreal y2 ); + void renderLine( const QPointF&, const QPointF& ); + void renderLine( const QLineF& ); + + const QskStippleMetrics& metrics() const; + + // nop: to be overloaded + virtual void renderDash( qreal x1, qreal y1, qreal x2, qreal y2 ); + + private: + const QskStippleMetrics m_metrics; +}; + +inline const QskStippleMetrics& QskStippledLineRenderer::metrics() const +{ + return m_metrics; +} + +#endif