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,
|
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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 );
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 );
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue