QskArcRenderer - work in progress, QskArcRenderNode added

This commit is contained in:
Uwe Rathmann 2024-05-14 17:03:03 +02:00
parent 49e1d54724
commit f6845c709c
6 changed files with 324 additions and 43 deletions

View File

@ -87,16 +87,18 @@ ArcPage::ArcPage( QQuickItem* parent )
arc->setSpanAngle( 270.0 );
arc->setThickness( 10.0 );
arc->setFillColor( Qt::darkRed );
arc->setFillColor( Qt::yellow );
arc->setBorderWidth( 0 );
arc->setBorderColor( Qt::darkYellow );
arc->setBorderWidth( 2.0 );
arc->setBorderColor( Qt::darkBlue );
#if 0
arc->setShadowColor( Qt::black );
arc->setSpreadRadius( 0.0 );
arc->setBlurRadius( 4.0 );
arc->setOffsetX( 2.0 );
arc->setOffsetY( 2.0 );
#endif
}
auto panel = new ControlPanel( arc );

View File

@ -102,6 +102,7 @@ list(APPEND SOURCES
list(APPEND HEADERS
nodes/QskArcNode.h
nodes/QskArcRenderer.h
nodes/QskArcRenderNode.h
nodes/QskArcShadowNode.h
nodes/QskBasicLinesNode.h
nodes/QskBoxNode.h
@ -143,6 +144,7 @@ list(APPEND PRIVATE_HEADERS
list(APPEND SOURCES
nodes/QskArcNode.cpp
nodes/QskArcRenderer.cpp
nodes/QskArcRenderNode.cpp
nodes/QskArcShadowNode.cpp
nodes/QskBasicLinesNode.cpp
nodes/QskBoxNode.cpp

View File

@ -6,6 +6,7 @@
#include "QskArcNode.h"
#include "QskArcMetrics.h"
#include "QskArcShadowNode.h"
#include "QskArcRenderNode.h"
#include "QskMargins.h"
#include "QskGradient.h"
#include "QskShapeNode.h"
@ -13,9 +14,18 @@
#include "QskSGNode.h"
#include "QskShadowMetrics.h"
#include <qpen.h>
#include <qpainterpath.h>
#define ARC_RENDERER
#ifdef ARC_RENDERER
using BorderNode = QskArcRenderNode;
#else
#include <qpen.h>
using BorderNode = QskStrokeNode;
#endif
namespace
{
enum NodeRole
@ -107,7 +117,7 @@ void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics
auto fillNode = static_cast< QskShapeNode* >(
QskSGNode::findChildNode( this, FillRole ) );
auto borderNode = static_cast< QskStrokeNode* >(
auto borderNode = static_cast< BorderNode* >(
QskSGNode::findChildNode( this, BorderRole ) );
const auto arcRect = qskEffectiveRect( rect, borderWidth );
@ -173,14 +183,19 @@ void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics
{
if ( borderNode == nullptr )
{
borderNode = new QskStrokeNode;
borderNode = new BorderNode;
QskSGNode::setNodeRole( borderNode, BorderRole );
}
#ifdef ARC_RENDERER
borderNode->updateNode( arcRect, metricsArc, borderWidth,
borderColor, gradient );
#else
QPen pen( borderColor, borderWidth );
pen.setCapStyle( Qt::FlatCap );
borderNode->updateNode( path, QTransform(), pen );
#endif
}
else
{
@ -188,7 +203,7 @@ void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics
borderNode = nullptr;
}
qskUpdateChildren(this, ShadowRole, shadowNode);
qskUpdateChildren(this, FillRole, fillNode);
qskUpdateChildren(this, BorderRole, borderNode);
qskUpdateChildren( this, ShadowRole, shadowNode );
qskUpdateChildren( this, FillRole, fillNode );
qskUpdateChildren( this, BorderRole, borderNode );
}

View File

@ -0,0 +1,95 @@
/******************************************************************************
* QSkinny - Copyright (C) The authors
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "QskArcRenderNode.h"
#include "QskGradient.h"
#include "QskArcRenderer.h"
#include "QskArcMetrics.h"
#include "QskGradient.h"
#include "QskSGNode.h"
QSK_QT_PRIVATE_BEGIN
#include <private/qsgnode_p.h>
QSK_QT_PRIVATE_END
#if 1
#include <qsgvertexcolormaterial.h>
#include <qglobalstatic.h>
// deriving from QskFillNode:TODO ...
Q_GLOBAL_STATIC( QSGVertexColorMaterial, qskMaterialColorVertex )
#endif
class QskArcRenderNodePrivate final : public QSGGeometryNodePrivate
{
public:
QskArcRenderNodePrivate()
: geometry( QSGGeometry::defaultAttributes_ColoredPoint2D(), 0 )
{
}
inline void resetValues()
{
hash = 0;
}
QSGGeometry geometry;
QskHashValue hash = 0;
};
QskArcRenderNode::QskArcRenderNode()
: QSGGeometryNode( *new QskArcRenderNodePrivate )
{
Q_D( QskArcRenderNode );
setGeometry( &d->geometry );
setMaterial( qskMaterialColorVertex );
setFlag( QSGNode::OwnsMaterial, false );
}
void QskArcRenderNode::updateNode(
const QRectF& rect, const QskArcMetrics& metrics, qreal borderWidth,
const QColor& borderColor, const QskGradient& gradient )
{
Q_D( QskArcRenderNode );
bool visible = !( rect.isEmpty() || metrics.isNull() );
if ( visible )
{
visible = gradient.isVisible();
if ( !visible )
{
visible = ( borderWidth > 0.0 )
&& borderColor.isValid() && ( borderColor.alpha() > 0 );
}
}
if ( !visible )
{
d->resetValues();
QskSGNode::resetGeometry( this );
return;
}
QskHashValue hash = 3496;
hash = qHashBits( &rect, sizeof( QRectF ), hash );
hash = qHash( borderWidth, hash );
hash = qHash( borderColor.rgba(), hash );
hash = metrics.hash( hash );
hash = gradient.hash( hash );
if ( hash != d->hash )
{
d->hash = hash;
QskArcRenderer::renderBorderGeometry(
rect, metrics, borderWidth, *geometry() );
markDirty( QSGNode::DirtyGeometry );
markDirty( QSGNode::DirtyMaterial );
}
}

View File

@ -0,0 +1,31 @@
/******************************************************************************
* QSkinny - Copyright (C) The authors
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#ifndef QSK_ARC_RENDER_NODE_H
#define QSK_ARC_RENDER_NODE_H
#include "QskGlobal.h"
#include <qsgnode.h>
class QskGradient;
class QskArcMetrics;
class QskArcRenderNodePrivate;
class QSK_EXPORT QskArcRenderNode : public QSGGeometryNode
{
using Inherited = QSGGeometryNode;
public:
QskArcRenderNode();
void updateNode( const QRectF&, const QskArcMetrics&, qreal borderWidth,
const QColor& borderColor, const QskGradient& );
private:
Q_DECLARE_PRIVATE( QskArcRenderNode )
};
#endif

View File

@ -10,6 +10,10 @@
#include <qsggeometry.h>
#if 1
#include <qdebug.h>
#endif
static inline QskVertex::Line* qskAllocateLines(
QSGGeometry& geometry, int lineCount )
{
@ -44,8 +48,75 @@ static inline int qskApproximatedCircumference( const QRectF& rect )
static inline int qskStepCount( const QRectF& rect )
{
#if 0
const auto dist = 3.0;
#else
const auto dist = 20.0;
#endif
const int length = qskApproximatedCircumference( rect );
return std::max( 3, qCeil( length / 3.0 ) );
return std::max( 3, qCeil( length / dist ) );
}
namespace
{
class AngleIterator
{
public:
AngleIterator( qreal radians1, qreal radians2, int stepCount );
inline void operator++() { increment(); }
void increment();
inline double cos() const { return m_cos; }
inline double sin() const { return m_sin; }
inline int step() const { return m_stepIndex; }
inline int stepCount() const { return m_stepCount; }
inline bool isDone() const { return m_stepIndex > m_stepCount; }
private:
double m_cos;
double m_sin;
int m_stepIndex;
int m_stepCount;
const double m_radians1;
const double m_radians2;
const double m_radiansStep;
};
inline AngleIterator::AngleIterator( qreal radians1, qreal radians2, int stepCount )
: m_stepIndex( 0 )
, m_stepCount( stepCount )
, m_radians1( radians1 )
, m_radians2( radians2 )
, m_radiansStep( ( radians2 - radians1 ) / stepCount )
{
m_cos = qFastCos( radians1 );
m_sin = qFastSin( radians1 );
}
inline void AngleIterator::increment()
{
if ( ++m_stepIndex >= m_stepCount )
{
if ( m_stepIndex == m_stepCount )
{
m_cos = qFastCos( m_radians2 );
m_sin = qFastSin( m_radians2 );
}
}
else
{
const auto radians = m_radians1 + m_stepIndex * m_radiansStep;
m_cos = qFastCos( radians );
m_sin = qFastSin( radians );
}
}
}
namespace
@ -58,49 +129,106 @@ namespace
int fillCount() const;
int borderCount() const;
void setBorderLines( QskVertex::Line* ) const;
int setBorderLines( QskVertex::ColoredLine* ) const;
private:
int arcLineCount() const;
void setArcLines( QskVertex::ColoredLine*, int lineCount,
const QPointF&, const qreal width, const qreal height,
const qreal radians1, const qreal radians2,
qreal arcWidth, const QskVertex::Color ) const;
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() )
Stroker::Stroker( const QRectF& rect,
const QskArcMetrics& metrics, qreal borderWidth )
: m_rect( rect )
, m_metrics( metrics )
, m_borderWidth( borderWidth )
{
n = qCeil( n * qAbs( m_metrics.spanAngle() ) / 360.0 );
n += 1; // closing line
Q_ASSERT( metrics.sizeMode() == Qt::AbsoluteSize );
}
n *= 2; // inner/outer border
n += 1; // dummy line connection inner/outer border
int Stroker::fillCount() const
{
return 0; // TODO
}
return n;
}
int Stroker::arcLineCount() const
{
if ( m_metrics.isNull() )
return 0;
void Stroker::setBorderLines( QskVertex::Line* lines ) const
{
Q_UNUSED( lines );
int n = qskStepCount( m_rect );
if ( !m_metrics.isClosed() )
n = qCeil( n * qAbs( m_metrics.spanAngle() ) / 360.0 );
return n;
}
int Stroker::borderCount() const
{
if ( m_metrics.isNull() )
return 0;
return 2 * arcLineCount() + 1;
}
void Stroker::setArcLines( QskVertex::ColoredLine* lines, int lineCount,
const QPointF& center, const qreal w, const qreal h,
const qreal radians1, const qreal radians2,
qreal arcWidth, const QskVertex::Color color ) const
{
const auto w2 = w - arcWidth;
const auto h2 = h - arcWidth;
auto l = lines;
for ( AngleIterator it( radians1, radians2, lineCount - 1 ); !it.isDone(); ++it )
{
const auto x1 = center.x() + w * it.cos();
const auto x2 = center.x() + w2 * it.cos();
const auto y1 = center.y() + h * it.sin();
const auto y2 = center.y() + h2 * it.sin();
l++->setLine( x1, y1, x2, y2, color );
}
if ( l - lines != lineCount )
qWarning() << lineCount << "->" << l - lines;
Q_ASSERT( l - lines == lineCount );
}
int Stroker::setBorderLines( QskVertex::ColoredLine* lines ) const
{
const auto center = m_rect.center();
const qreal radians1 = qDegreesToRadians( m_metrics.startAngle() );
const qreal radians2 = qDegreesToRadians( m_metrics.endAngle() );
const int n = arcLineCount();
const QskVertex::Color color( QColor( Qt::darkBlue ) );
auto w = 0.5 * m_rect.width();
auto h = 0.5 * m_rect.height();
setArcLines( lines, n, center, w, h, radians1, radians2, m_borderWidth, color );
w -= m_metrics.thickness() - m_borderWidth;
h -= m_metrics.thickness() - m_borderWidth;
setArcLines( lines + n + 1, n, center, w, h, radians2, radians1, m_borderWidth, color );
lines[n] = { lines[n - 1].p2, lines[n + 1].p1 };
return 2 * n + 1;
}
}
void QskArcRenderer::renderBorderGeometry( const QRectF& rect,
@ -110,9 +238,17 @@ void QskArcRenderer::renderBorderGeometry( const QRectF& rect,
Stroker stroker( rect, metrics, borderWidth );
const auto lines = qskAllocateLines( geometry, stroker.borderCount() );
const auto lineCount = stroker.borderCount();
const auto lines = qskAllocateColoredLines( geometry, lineCount );
if ( lines )
stroker.setBorderLines( lines );
{
const auto effectiveCount = stroker.setBorderLines( lines );
if ( lineCount != effectiveCount )
{
qWarning() << lineCount << effectiveCount;
}
}
}
void QskArcRenderer::renderFillGeometry( const QRectF& rect,