From 49e1d547243af5e78af3d573e43b5bd0f0430c72 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Wed, 24 Apr 2024 11:25:06 +0200 Subject: [PATCH] QskArcRenderer started with some boilerplate code --- src/CMakeLists.txt | 2 + src/nodes/QskArcRenderer.cpp | 164 +++++++++++++++++++++++++++++++++++ src/nodes/QskArcRenderer.h | 51 +++++++++++ 3 files changed, 217 insertions(+) create mode 100644 src/nodes/QskArcRenderer.cpp create mode 100644 src/nodes/QskArcRenderer.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index de2dd310..4674eb23 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -101,6 +101,7 @@ list(APPEND SOURCES list(APPEND HEADERS nodes/QskArcNode.h + nodes/QskArcRenderer.h nodes/QskArcShadowNode.h nodes/QskBasicLinesNode.h nodes/QskBoxNode.h @@ -141,6 +142,7 @@ list(APPEND PRIVATE_HEADERS list(APPEND SOURCES nodes/QskArcNode.cpp + nodes/QskArcRenderer.cpp nodes/QskArcShadowNode.cpp nodes/QskBasicLinesNode.cpp nodes/QskBoxNode.cpp diff --git a/src/nodes/QskArcRenderer.cpp b/src/nodes/QskArcRenderer.cpp new file mode 100644 index 00000000..9ff20905 --- /dev/null +++ b/src/nodes/QskArcRenderer.cpp @@ -0,0 +1,164 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskArcRenderer.h" +#include "QskArcMetrics.h" +#include "QskGradient.h" +#include "QskVertex.h" + +#include + +static inline QskVertex::Line* qskAllocateLines( + QSGGeometry& geometry, int lineCount ) +{ + geometry.allocate( 2 * lineCount ); // 2 points per line + return reinterpret_cast< QskVertex::Line* >( geometry.vertexData() ); +} + +static inline QskVertex::ColoredLine* qskAllocateColoredLines( + QSGGeometry& geometry, int lineCount ) +{ + geometry.allocate( 2 * lineCount ); // 2 points per line + return reinterpret_cast< QskVertex::ColoredLine* >( geometry.vertexData() ); +} + +static inline int qskApproximatedCircumference( const QRectF& rect ) +{ + const qreal a = rect.width(); + const qreal b = rect.height(); + + const auto ratio = a / b; + if ( ratio > 0.9 || ratio < 1.1 ) + return std::max( a, b ) * 2.0 * M_PI; // circle + + // Srinivasa Ramanujan: https://en.wikipedia.org/wiki/Ellipse#Circumference + + const qreal d1 = ( a - b ); + const qreal d2 = ( a + b ); + const qreal h = ( d1 * d1 ) / ( d2 * d2 ); + + return M_PI * d2 * ( 1.0 + ( 3 * h / ( 10 + sqrt( 4 - 3 * h ) ) ) ); +} + +static inline int qskStepCount( const QRectF& rect ) +{ + const int length = qskApproximatedCircumference( rect ); + return std::max( 3, qCeil( length / 3.0 ) ); +} + +namespace +{ + class Stroker + { + public: + Stroker( const QRectF&, const QskArcMetrics& metrics, qreal borderWidth ); + + int fillCount() const; + int borderCount() const; + + void setBorderLines( QskVertex::Line* ) const; + + private: + const QRectF& m_rect; + const QskArcMetrics& m_metrics; + const qreal m_borderWidth; + }; +} + +Stroker::Stroker( const QRectF& rect, const QskArcMetrics& metrics, qreal borderWidth ) + : m_rect( rect ) + , m_metrics( metrics ) + , m_borderWidth( borderWidth ) +{ + Q_ASSERT( metrics.sizeMode() == Qt::AbsoluteSize ); +} + +int Stroker::fillCount() const +{ + return 0; // TODO +} + +int Stroker::borderCount() const +{ + if ( m_metrics.isNull() ) + return 0; + + int n = qskStepCount( m_rect ); + if ( !m_metrics.isClosed() ) + { + n = qCeil( n * qAbs( m_metrics.spanAngle() ) / 360.0 ); + n += 1; // closing line + } + + n *= 2; // inner/outer border + n += 1; // dummy line connection inner/outer border + + return n; +} + +void Stroker::setBorderLines( QskVertex::Line* lines ) const +{ + Q_UNUSED( lines ); +} + +void QskArcRenderer::renderBorderGeometry( const QRectF& rect, + const QskArcMetrics& metrics, qreal borderWidth, QSGGeometry& geometry ) +{ + geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip ); + + Stroker stroker( rect, metrics, borderWidth ); + + const auto lines = qskAllocateLines( geometry, stroker.borderCount() ); + if ( lines ) + stroker.setBorderLines( lines ); +} + +void QskArcRenderer::renderFillGeometry( const QRectF& rect, + const QskArcMetrics& metrics, qreal borderWidth, QSGGeometry& geometry ) +{ + Q_UNUSED( rect ); + Q_UNUSED( geometry ); + + Stroker stroker( rect, metrics, borderWidth ); + + const auto lines = qskAllocateColoredLines( geometry, stroker.fillCount() ); + if ( lines ) + { + // TODO + } +} + +void QskArcRenderer::renderFillGeometry( const QRectF& rect, + const QskArcMetrics& metrics, QSGGeometry& geometry ) +{ + renderFillGeometry( rect, metrics, 0.0, geometry ); +} + +bool QskArcRenderer::isGradientSupported( + const QskArcMetrics& metrics, const QskGradient& gradient ) +{ + if ( metrics.isNull() || !gradient.isVisible() || gradient.isMonochrome() ) + return true; + + return gradient.type() == QskGradient::Stops; +} + +void QskArcRenderer::renderArc( const QRectF& rect, + const QskArcMetrics& metrics, qreal borderWidth, const QColor& borderColor, + const QskGradient& gradient, QSGGeometry& geometry ) +{ + Q_UNUSED( rect ); + Q_UNUSED( metrics ); + Q_UNUSED( borderWidth ); + Q_UNUSED( borderColor ); + Q_UNUSED( gradient ); + Q_UNUSED( geometry ); +} + +void QskArcRenderer::renderArc( const QRectF& rect, const QskArcMetrics& metrics, + const QskGradient& gradient, QSGGeometry& geometry ) +{ + renderArc( rect, metrics, 0, QColor(), gradient, geometry ); +} diff --git a/src/nodes/QskArcRenderer.h b/src/nodes/QskArcRenderer.h new file mode 100644 index 00000000..d5429dbb --- /dev/null +++ b/src/nodes/QskArcRenderer.h @@ -0,0 +1,51 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_ARC_RENDERER_H +#define QSK_ARC_RENDERER_H + +#include "QskGlobal.h" + +class QskArcMetrics; +class QskGradient; + +class QSGGeometry; +class QRectF; +class QColor; + +namespace QskArcRenderer +{ + /* + Filling the geometry without any color information: + see QSGGeometry::defaultAttributes_Point2D() + + - clip nodes + - using shaders setting the color information + */ + + QSK_EXPORT void renderBorderGeometry( const QRectF&, + const QskArcMetrics&, qreal borderWidth, QSGGeometry& ); + + QSK_EXPORT void renderFillGeometry( const QRectF&, + const QskArcMetrics&, qreal borderWidth, QSGGeometry& ); + + QSK_EXPORT void renderFillGeometry( const QRectF&, + const QskArcMetrics&, QSGGeometry& ); + + /* + Filling the geometry usually with color information: + see QSGGeometry::defaultAttributes_ColoredPoint2D() + */ + QSK_EXPORT bool isGradientSupported( const QskArcMetrics&, const QskGradient& ); + + QSK_EXPORT void renderArc( const QRectF&, + const QskArcMetrics&, qreal borderWidth, const QColor& borderColor, + const QskGradient&, QSGGeometry& ); + + QSK_EXPORT void renderArc( const QRectF&, const QskArcMetrics&, + const QskGradient&, QSGGeometry& ); +} + +#endif