QskArcNode is a QskShapeNode now.

The performance of the previous implementation was simply horrible,
when drawing an arc with a small span angle. The size of the corresponding full circle
is huge and the previous implementation always created an image/texture with
that size.

However the final implementation is supposed to create vertex lists
- like what the box renderer does. So this code will also not stay forever.
This commit is contained in:
Uwe Rathmann 2023-04-12 12:19:26 +02:00
parent 76248e480b
commit 45a1bc3564
6 changed files with 100 additions and 89 deletions

View File

@ -326,7 +326,7 @@ Skin::Palette DaytimeSkin::palette() const
0xffc4c4c4, 0xffc4c4c4,
{ { { 0.0, 0xffff3122 }, { 0.2, 0xfffeeeb7 }, { 0.3, 0xffa7b0ff }, { 0.5, 0xff6776ff }, { { { 0.0, 0xffff3122 }, { 0.2, 0xfffeeeb7 }, { 0.3, 0xffa7b0ff }, { 0.5, 0xff6776ff },
{ 1.0, Qt::black } } }, { 1.0, Qt::black } } },
{ { { 0.0, 0xffc4c4c4 }, { 0.5, 0xfff8f8f8 }, { 1.0, 0xffc4c4c4 } } }, { { { 0.0, 0xffe0e0e0 }, { 0.5, 0xfff8f8f8 }, { 1.0, 0xffe0e0e0 } } },
0xffdddddd, 0xffdddddd,
}; };
} }

View File

@ -204,16 +204,15 @@ static inline QSGNode* qskUpdateBoxNode(
} }
static inline QSGNode* qskUpdateArcNode( static inline QSGNode* qskUpdateArcNode(
const QskSkinnable* skinnable, QSGNode* node, const QRectF& rect, const QskSkinnable*, QSGNode* node, const QRectF& rect,
const QskGradient& fillGradient, const QskArcMetrics& metrics ) const QskGradient& fillGradient, const QskArcMetrics& metrics )
{ {
const auto control = skinnable->owningControl(); if ( rect.isEmpty() )
if ( control == nullptr || rect.isEmpty() )
return nullptr; return nullptr;
auto absoluteMetrics = metrics.toAbsolute( rect.size() ); const auto absoluteMetrics = metrics.toAbsolute( rect.size() );
if ( !qskIsArcVisible( metrics, fillGradient ) ) if ( !qskIsArcVisible( absoluteMetrics, fillGradient ) )
return nullptr; return nullptr;
auto arcNode = static_cast< QskArcNode* >( node ); auto arcNode = static_cast< QskArcNode* >( node );
@ -221,8 +220,7 @@ static inline QSGNode* qskUpdateArcNode(
if ( arcNode == nullptr ) if ( arcNode == nullptr )
arcNode = new QskArcNode(); arcNode = new QskArcNode();
const auto r = qskSceneAlignedRect( control, rect ); arcNode->setArcData( rect, absoluteMetrics, fillGradient );
arcNode->setArcData( r, absoluteMetrics, fillGradient, control->window() );
return arcNode; return arcNode;
} }

View File

@ -4,18 +4,67 @@
*****************************************************************************/ *****************************************************************************/
#include "QskArcNode.h" #include "QskArcNode.h"
#include "QskArcMetrics.h"
#include "QskArcRenderer.h" #include "QskArcRenderer.h"
#include "QskArcMetrics.h"
#include "QskGradient.h" #include "QskGradient.h"
#include "QskGradientDirection.h"
namespace #include <qpainterpath.h>
static inline QskGradient effectiveGradient( const QRectF& rect,
const QskArcMetrics& metrics, const QskGradient& gradient )
{ {
class ArcData if ( gradient.isMonochrome() )
return gradient;
bool isRadial = false;
if ( gradient.type() == QskGradient::Linear )
{ {
public: /*
const QskArcMetrics& metrics; Horizontal is interpreted as conic ( in direction of the arc ),
const QskGradient& gradient; while Vertical means radial ( inner to outer border )
}; */
isRadial = gradient.linearDirection().isVertical();
}
auto g = gradient;
g.setStretchMode( QskGradient::NoStretch );
const auto center = rect.center();
if( isRadial )
{
g.setRadialDirection( center.x(), center.y(),
rect.width(), rect.height() );
{
/*
Trying to do what QGradient::LogicalMode does in
the previous QPainter based implementation
*/
const auto radius = 0.5 * qMin( rect.width(), rect.height() );
const auto t = metrics.thickness() / radius;
QskGradientStops stops;
stops.reserve( gradient.stops().size() );
for ( const auto& stop : gradient.stops() )
{
const auto pos = 0.5 - t * ( 0.75 - stop.position() );
stops += QskGradientStop( pos, stop.color() );
}
g.setStops( stops );
}
}
else
{
g.setConicDirection( center.x(), center.y(), metrics.startAngle() );
}
return g;
} }
QskArcNode::QskArcNode() QskArcNode::QskArcNode()
@ -26,27 +75,25 @@ QskArcNode::~QskArcNode()
{ {
} }
void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& metrics, void QskArcNode::setArcData( const QRectF& rect,
const QskGradient& gradient, QQuickWindow* window ) const QskArcMetrics& metrics, const QskGradient& gradient )
{ {
const ArcData arcData { metrics, gradient }; #if 1
update( window, rect, QSizeF(), &arcData ); /*
} Translating linear gradients into conic or radial gradients.
This code is a leftover from situations, where only linear
void QskArcNode::paint( QPainter* painter, const QSize& size, const void* nodeData ) gradients had been available. Once the iotdashboard example
{ has been adjusted we will remove this code TODO ...
const auto arcData = reinterpret_cast< const ArcData* >( nodeData ); */
const auto g = effectiveGradient( rect, metrics, gradient );
const qreal t = arcData->metrics.thickness(); #endif
const QRectF rect( 0.5 * t, 0.5 * t, size.width() - t, size.height() - t );
/*
QskArcRenderer::renderArc( rect, arcData->metrics, arcData->gradient, painter ); For the moment using a QPainterPath/QskShapeNode.
} But we can do better by creatig vertex lists manually
like what is done by the box renderer. TODO ...
QskHashValue QskArcNode::hash( const void* nodeData ) const */
{
const auto arcData = reinterpret_cast< const ArcData* >( nodeData ); const auto path = QskArcRenderer::arcPath( rect, metrics );
updateNode( path, QTransform(), rect, g );
auto h = arcData->metrics.hash();
return arcData->gradient.hash( h );
} }

View File

@ -6,25 +6,18 @@
#ifndef QSK_ARC_NODE_H #ifndef QSK_ARC_NODE_H
#define QSK_ARC_NODE_H #define QSK_ARC_NODE_H
#include "QskPaintedNode.h" #include "QskShapeNode.h"
class QskArcMetrics; class QskArcMetrics;
class QskGradient; class QskGradient;
// should be a QSGGeometryNode, TODO .. class QSK_EXPORT QskArcNode : public QskShapeNode
class QSK_EXPORT QskArcNode : public QskPaintedNode
{ {
public: public:
QskArcNode(); QskArcNode();
~QskArcNode() override; ~QskArcNode() override;
void setArcData( const QRectF&, const QskArcMetrics&, void setArcData( const QRectF&, const QskArcMetrics&, const QskGradient& );
const QskGradient&, QQuickWindow* );
protected:
void paint( QPainter*, const QSize&, const void* nodeData ) override;
QskHashValue hash( const void* nodeData ) const override;
}; };
#endif #endif

View File

@ -5,50 +5,25 @@
#include "QskArcRenderer.h" #include "QskArcRenderer.h"
#include "QskArcMetrics.h" #include "QskArcMetrics.h"
#include "QskGradient.h"
#include "QskGradientDirection.h"
#include <qpainter.h> #include <qpainterpath.h>
#include <qrect.h> #include <qrect.h>
void QskArcRenderer::renderArc(const QRectF& rect, QPainterPath QskArcRenderer::arcPath(
const QskArcMetrics& metrics, const QskGradient& gradient, QPainter* painter ) const QRectF& rect, const QskArcMetrics& metrics )
{ {
bool isRadial = false; const auto m = metrics.toAbsolute( rect.size() );
if ( gradient.type() == QskGradient::Linear ) const qreal t2 = 0.5 * m.thickness();
{ const auto r = rect.adjusted( t2, t2, -t2, -t2 );
/*
Horizontal is interpreted as conic ( in direction of the arc ),
while Vertical means radial ( inner to outer border )
*/
isRadial = gradient.linearDirection().isVertical();
}
QBrush brush; QPainterPath path;
path.arcMoveTo( r, m.startAngle() );
path.arcTo( r, m.startAngle(), m.spanAngle() );
const auto qStops = qskToQGradientStops( gradient.stops() ); QPainterPathStroker stroker;
stroker.setWidth( m.thickness() );
stroker.setCapStyle( Qt::FlatCap );
if( isRadial ) return stroker.createStroke( path );
{
QRadialGradient radial( rect.center(), qMin( rect.width(), rect.height() ) );
radial.setStops( qStops );
brush = radial;
}
else
{
QConicalGradient conical( rect.center(), metrics.startAngle() );
conical.setStops( qStops );
brush = conical;
}
painter->setRenderHint( QPainter::Antialiasing, true );
painter->setPen( QPen( brush, metrics.thickness(), Qt::SolidLine, Qt::FlatCap ) );
const int startAngle = qRound( metrics.startAngle() * 16 );
const int spanAngle = qRound( metrics.spanAngle() * 16 );
painter->drawArc( rect, startAngle, spanAngle );
} }

View File

@ -9,15 +9,13 @@
#include "QskGlobal.h" #include "QskGlobal.h"
class QskArcMetrics; class QskArcMetrics;
class QskGradient;
class QPainter; class QPainterPath;
class QRectF; class QRectF;
namespace QskArcRenderer namespace QskArcRenderer
{ {
QSK_EXPORT void renderArc( const QRectF&, const QskArcMetrics&, QSK_EXPORT QPainterPath arcPath( const QRectF&, const QskArcMetrics& );
const QskGradient&, QPainter* );
}; };
#endif #endif