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:
parent
76248e480b
commit
45a1bc3564
|
@ -326,7 +326,7 @@ Skin::Palette DaytimeSkin::palette() const
|
|||
0xffc4c4c4,
|
||||
{ { { 0.0, 0xffff3122 }, { 0.2, 0xfffeeeb7 }, { 0.3, 0xffa7b0ff }, { 0.5, 0xff6776ff },
|
||||
{ 1.0, Qt::black } } },
|
||||
{ { { 0.0, 0xffc4c4c4 }, { 0.5, 0xfff8f8f8 }, { 1.0, 0xffc4c4c4 } } },
|
||||
{ { { 0.0, 0xffe0e0e0 }, { 0.5, 0xfff8f8f8 }, { 1.0, 0xffe0e0e0 } } },
|
||||
0xffdddddd,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -204,16 +204,15 @@ static inline QSGNode* qskUpdateBoxNode(
|
|||
}
|
||||
|
||||
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 auto control = skinnable->owningControl();
|
||||
if ( control == nullptr || rect.isEmpty() )
|
||||
if ( rect.isEmpty() )
|
||||
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;
|
||||
|
||||
auto arcNode = static_cast< QskArcNode* >( node );
|
||||
|
@ -221,8 +220,7 @@ static inline QSGNode* qskUpdateArcNode(
|
|||
if ( arcNode == nullptr )
|
||||
arcNode = new QskArcNode();
|
||||
|
||||
const auto r = qskSceneAlignedRect( control, rect );
|
||||
arcNode->setArcData( r, absoluteMetrics, fillGradient, control->window() );
|
||||
arcNode->setArcData( rect, absoluteMetrics, fillGradient );
|
||||
|
||||
return arcNode;
|
||||
}
|
||||
|
|
|
@ -4,18 +4,67 @@
|
|||
*****************************************************************************/
|
||||
|
||||
#include "QskArcNode.h"
|
||||
#include "QskArcMetrics.h"
|
||||
#include "QskArcRenderer.h"
|
||||
#include "QskArcMetrics.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;
|
||||
const QskGradient& gradient;
|
||||
};
|
||||
/*
|
||||
Horizontal is interpreted as conic ( in direction of the arc ),
|
||||
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()
|
||||
|
@ -26,27 +75,25 @@ QskArcNode::~QskArcNode()
|
|||
{
|
||||
}
|
||||
|
||||
void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& metrics,
|
||||
const QskGradient& gradient, QQuickWindow* window )
|
||||
void QskArcNode::setArcData( const QRectF& rect,
|
||||
const QskArcMetrics& metrics, const QskGradient& gradient )
|
||||
{
|
||||
const ArcData arcData { metrics, gradient };
|
||||
update( window, rect, QSizeF(), &arcData );
|
||||
}
|
||||
|
||||
void QskArcNode::paint( QPainter* painter, const QSize& size, const void* nodeData )
|
||||
{
|
||||
const auto arcData = reinterpret_cast< const ArcData* >( nodeData );
|
||||
|
||||
const qreal t = arcData->metrics.thickness();
|
||||
const QRectF rect( 0.5 * t, 0.5 * t, size.width() - t, size.height() - t );
|
||||
|
||||
QskArcRenderer::renderArc( rect, arcData->metrics, arcData->gradient, painter );
|
||||
}
|
||||
|
||||
QskHashValue QskArcNode::hash( const void* nodeData ) const
|
||||
{
|
||||
const auto arcData = reinterpret_cast< const ArcData* >( nodeData );
|
||||
|
||||
auto h = arcData->metrics.hash();
|
||||
return arcData->gradient.hash( h );
|
||||
#if 1
|
||||
/*
|
||||
Translating linear gradients into conic or radial gradients.
|
||||
This code is a leftover from situations, where only linear
|
||||
gradients had been available. Once the iotdashboard example
|
||||
has been adjusted we will remove this code TODO ...
|
||||
*/
|
||||
const auto g = effectiveGradient( rect, metrics, gradient );
|
||||
#endif
|
||||
|
||||
/*
|
||||
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 ...
|
||||
*/
|
||||
|
||||
const auto path = QskArcRenderer::arcPath( rect, metrics );
|
||||
updateNode( path, QTransform(), rect, g );
|
||||
}
|
||||
|
|
|
@ -6,25 +6,18 @@
|
|||
#ifndef QSK_ARC_NODE_H
|
||||
#define QSK_ARC_NODE_H
|
||||
|
||||
#include "QskPaintedNode.h"
|
||||
#include "QskShapeNode.h"
|
||||
|
||||
class QskArcMetrics;
|
||||
class QskGradient;
|
||||
|
||||
// should be a QSGGeometryNode, TODO ..
|
||||
|
||||
class QSK_EXPORT QskArcNode : public QskPaintedNode
|
||||
class QSK_EXPORT QskArcNode : public QskShapeNode
|
||||
{
|
||||
public:
|
||||
QskArcNode();
|
||||
~QskArcNode() override;
|
||||
|
||||
void setArcData( const QRectF&, const QskArcMetrics&,
|
||||
const QskGradient&, QQuickWindow* );
|
||||
|
||||
protected:
|
||||
void paint( QPainter*, const QSize&, const void* nodeData ) override;
|
||||
QskHashValue hash( const void* nodeData ) const override;
|
||||
void setArcData( const QRectF&, const QskArcMetrics&, const QskGradient& );
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -5,50 +5,25 @@
|
|||
|
||||
#include "QskArcRenderer.h"
|
||||
#include "QskArcMetrics.h"
|
||||
#include "QskGradient.h"
|
||||
#include "QskGradientDirection.h"
|
||||
|
||||
#include <qpainter.h>
|
||||
#include <qpainterpath.h>
|
||||
#include <qrect.h>
|
||||
|
||||
void QskArcRenderer::renderArc(const QRectF& rect,
|
||||
const QskArcMetrics& metrics, const QskGradient& gradient, QPainter* painter )
|
||||
QPainterPath QskArcRenderer::arcPath(
|
||||
const QRectF& rect, const QskArcMetrics& metrics )
|
||||
{
|
||||
bool isRadial = false;
|
||||
const auto m = metrics.toAbsolute( rect.size() );
|
||||
|
||||
if ( gradient.type() == QskGradient::Linear )
|
||||
{
|
||||
/*
|
||||
Horizontal is interpreted as conic ( in direction of the arc ),
|
||||
while Vertical means radial ( inner to outer border )
|
||||
*/
|
||||
isRadial = gradient.linearDirection().isVertical();
|
||||
}
|
||||
const qreal t2 = 0.5 * m.thickness();
|
||||
const auto r = rect.adjusted( t2, t2, -t2, -t2 );
|
||||
|
||||
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 )
|
||||
{
|
||||
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 );
|
||||
return stroker.createStroke( path );
|
||||
}
|
||||
|
|
|
@ -9,15 +9,13 @@
|
|||
#include "QskGlobal.h"
|
||||
|
||||
class QskArcMetrics;
|
||||
class QskGradient;
|
||||
|
||||
class QPainter;
|
||||
class QPainterPath;
|
||||
class QRectF;
|
||||
|
||||
namespace QskArcRenderer
|
||||
{
|
||||
QSK_EXPORT void renderArc( const QRectF&, const QskArcMetrics&,
|
||||
const QskGradient&, QPainter* );
|
||||
QSK_EXPORT QPainterPath arcPath( const QRectF&, const QskArcMetrics& );
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue