calculating vertexes manually instead of using the expensive painter

path triangulator
This commit is contained in:
Uwe Rathmann 2022-12-05 11:39:09 +01:00
parent 4888fcc5ba
commit d82f3d08ff
4 changed files with 114 additions and 164 deletions

View File

@ -15,8 +15,6 @@
QSK_QT_PRIVATE_BEGIN
#include <private/qsgnode_p.h>
#include <private/qvectorpath_p.h>
#include <private/qtriangulator_p.h>
QSK_QT_PRIVATE_END
static inline QskHashValue qskMetricsHash(
@ -43,35 +41,6 @@ static inline QskGradient qskEffectiveGradient( const QskGradient& gradient )
return gradient;
}
static void qskUpdateGeometry( const QVector< qreal > path, QSGGeometry& geometry )
{
const auto hints = QVectorPath::PolygonHint | QVectorPath::OddEvenFill;
const auto ts = qTriangulate(
path.constData(), path.size() / 2, hints, QTransform(), false );
/*
As we have to iterate over the vertex buffer to copy qreal to float
anyway we reorder and drop the index buffer.
QTriangleSet:
vertices: (x[i[n]], y[i[n]]), (x[j[n]], y[j[n]]), (x[k[n]], y[k[n]]), n = 0, 1, ...
QVector<qreal> vertices; // [x[0], y[0], x[1], y[1], x[2], ...]
QVector<quint16> indices; // [i[0], j[0], k[0], i[1], j[1], k[1], i[2], ...]
*/
const auto points = ts.vertices.constData();
const auto indices = reinterpret_cast< const quint16* >( ts.indices.data() );
geometry.allocate( ts.indices.size() );
auto vertexData = geometry.vertexDataAsPoint2D();
for ( int i = 0; i < ts.indices.size(); i++ )
{
const int j = 2 * indices[i];
vertexData[i].set( points[j], points[j + 1] );
}
}
static inline void qskResetGeometry( QskBoxFillNode* node )
{
auto g = node->geometry();
@ -88,7 +57,6 @@ class QskBoxFillNodePrivate final : public QSGGeometryNodePrivate
QskBoxFillNodePrivate()
: geometry( QSGGeometry::defaultAttributes_Point2D(), 0 )
{
geometry.setDrawingMode( QSGGeometry::DrawTriangles );
}
QskHashValue metricsHash = 0;
@ -138,17 +106,7 @@ void QskBoxFillNode::updateNode(
if ( dirtyGeometry )
{
const auto path = QskBoxRenderer().fillPath(
rect, shapeMetrics, borderMetrics );
if ( path.isEmpty() )
{
qskResetGeometry( this );
return;
}
qskUpdateGeometry( path, d->geometry );
QskBoxRenderer().renderFill( rect, shapeMetrics, borderMetrics, d->geometry );
markDirty( QSGNode::DirtyGeometry );
}

View File

@ -7,9 +7,7 @@
#define QSK_BOX_RENDERER_H
#include "QskBoxShapeMetrics.h"
#include <qrect.h>
#include <qvector.h>
class QskBoxBorderMetrics;
class QskBoxBorderColors;
@ -35,9 +33,6 @@ class QSK_EXPORT QskBoxRenderer
const QskBoxShapeMetrics&, const QskBoxBorderMetrics&,
const QskBoxBorderColors&, const QskGradient&, QSGGeometry& );
QVector< qreal > fillPath( const QRectF&,
const QskBoxShapeMetrics&, const QskBoxBorderMetrics& ) const;
class Quad
{
public:
@ -134,11 +129,6 @@ class QSK_EXPORT QskBoxRenderer
int lineCount, QskVertex::ColoredLine* );
void renderRectFill( const Quad&, const QskGradient&, QskVertex::ColoredLine* );
QVector< qreal > fillPathRect( const QRectF&, const QskBoxBorderMetrics& ) const;
QVector< qreal > fillPathRectellipse( const QRectF&,
const QskBoxShapeMetrics&, const QskBoxBorderMetrics& ) const;
};
inline void QskBoxRenderer::renderBorder(
@ -172,13 +162,4 @@ inline void QskBoxRenderer::renderBox( const QRectF& rect,
renderRectellipse( rect, shape, border, borderColors, gradient, geometry );
}
inline QVector< qreal > QskBoxRenderer::fillPath( const QRectF& rect,
const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& border ) const
{
if ( shape.isRectangle() )
return fillPathRect( rect, border );
else
return fillPathRectellipse( rect, shape, border );
}
#endif

View File

@ -1301,7 +1301,7 @@ void QskBoxRenderer::renderRectellipseFill(
if ( ( metrics.innerQuad.width <= 0 ) || ( metrics.innerQuad.height <= 0 ) )
{
allocateLines< Line >( geometry, 0 );
geometry.allocate( 0 );
return;
}
@ -1309,31 +1309,119 @@ void QskBoxRenderer::renderRectellipseFill(
{
// degenerated to a rectangle
const QRectF r( metrics.innerQuad.left, metrics.innerQuad.top,
metrics.innerQuad.width, metrics.innerQuad.height );
geometry.allocate( 4 );
renderRectFill( r, QskBoxBorderMetrics(), geometry );
const auto& quad = metrics.innerQuad;
auto p = geometry.vertexDataAsPoint2D();
p[0].set( quad.left, quad.top );
p[1].set( quad.right, quad.top );
p[2].set( quad.left, quad.bottom );
p[3].set( quad.right, quad.bottom );
return;
}
const int stepCount = metrics.corner[ 0 ].stepCount;
/*
Unfortunately QSGGeometry::DrawTriangleFan is no longer supported with
Qt6 and we have to go with DrawTriangleStrip, duplicating the center with
each each vertex.
*/
int lineCount = 2 * stepCount;
const auto numPoints =
metrics.corner[0].stepCount + metrics.corner[1].stepCount
+ metrics.corner[2].stepCount + metrics.corner[3].stepCount + 4;
/*
points: center point + interpolated corner points
indexes: lines between the center and each point, where
the first line needs to be appended to close the filling
*/
geometry.allocate( 1 + numPoints, 2 * ( numPoints + 1 ) );
Q_ASSERT( geometry.sizeOfIndex() == 2 );
auto p = geometry.vertexDataAsPoint2D();
auto idx = geometry.indexDataAsUShort();
int i = 0;
p[i++].set( rect.x() + 0.5 * rect.width(), rect.y() + 0.5 * rect.height() );
BorderValues v( metrics );
if ( metrics.centerQuad.top >= metrics.centerQuad.bottom )
{
// we need an extra line connecting the top/bottom corners
lineCount++;
constexpr auto id = TopLeft;
const auto& c = metrics.corner[ id ];
for ( ArcIterator it( c.stepCount, false ); !it.isDone(); ++it )
{
*idx++ = 0;
*idx++ = i;
v.setAngle( it.cos(), it.sin() );
p[i++].set( c.centerX - v.dx1( id ), c.centerY - v.dy1( id ) );
}
}
else
{
// for some reason we have 2 more lines for true ellipses ??
lineCount += 2;
constexpr auto id = BottomLeft;
const auto& c = metrics.corner[ id ];
for ( ArcIterator it( c.stepCount, true ); !it.isDone(); ++it )
{
*idx++ = 0;
*idx++ = i;
v.setAngle( it.cos(), it.sin() );
p[i++].set( c.centerX - v.dx1( id ), c.centerY + v.dy1( id ) );
}
}
{
constexpr auto id = BottomRight;
const auto& c = metrics.corner[ id ];
for ( ArcIterator it( c.stepCount, false ); !it.isDone(); ++it )
{
*idx++ = 0;
*idx++ = i;
v.setAngle( it.cos(), it.sin() );
p[i++].set( c.centerX + v.dx1( id ), c.centerY + v.dy1( id ) );
}
}
{
constexpr auto id = TopRight;
const auto& c = metrics.corner[ id ];
for ( ArcIterator it( c.stepCount, true ); !it.isDone(); ++it )
{
*idx++ = 0;
*idx++ = i;
v.setAngle( it.cos(), it.sin() );
p[i++].set( c.centerX + v.dx1( id ), c.centerY - v.dy1( id ) );
}
}
const auto line = allocateLines< Line >( geometry, lineCount );
qskRenderFillLines( metrics, Qt::Vertical, line, ColorMapNone() );
*idx++ = 0;
*idx++ = 1;
#if 0
{
auto p = geometry.vertexDataAsPoint2D();
qDebug() << "Vertexes:" << geometry.vertexCount();
for ( int i = 0; i < geometry.vertexCount(); i++ )
qDebug() << "\t" << i << p[i].x << p[i].y;
auto idx = geometry.indexDataAsUShort();
qDebug() << "Indexes:" << geometry.indexCount();
for ( int i = 0; i < geometry.indexCount(); i++ )
qDebug() << "\t" << i << idx[i];
}
#endif
}
void QskBoxRenderer::renderRectellipse( const QRectF& rect,
@ -1509,71 +1597,3 @@ void QskBoxRenderer::renderRectellipse( const QRectF& rect,
qskRenderBorder( metrics, Qt::Vertical, borderColors, line );
}
}
QVector< qreal > QskBoxRenderer::fillPathRectellipse( const QRectF& rect,
const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& border ) const
{
const Metrics metrics( rect, shape, border );
BorderValues v( metrics );
const auto totalSteps = metrics.corner[0].stepCount
+ metrics.corner[1].stepCount
+ metrics.corner[2].stepCount
+ metrics.corner[3].stepCount;
QVector< qreal > path;
path.resize( 2 * ( totalSteps + 4 ) );
auto p = path.data();
{
constexpr auto id = TopLeft;
const auto& c = metrics.corner[ id ];
for ( ArcIterator it( c.stepCount, false ); !it.isDone(); ++it )
{
v.setAngle( it.cos(), it.sin() );
*p++ = c.centerX - v.dx1( id );
*p++ =c.centerY - v.dy1( id );
}
}
{
constexpr auto id = BottomLeft;
const auto& c = metrics.corner[ id ];
for ( ArcIterator it( c.stepCount, true ); !it.isDone(); ++it )
{
v.setAngle( it.cos(), it.sin() );
*p++ = c.centerX - v.dx1( id );
*p++ = c.centerY + v.dy1( id );
}
}
{
constexpr auto id = BottomRight;
const auto& c = metrics.corner[ id ];
for ( ArcIterator it( c.stepCount, false ); !it.isDone(); ++it )
{
v.setAngle( it.cos(), it.sin() );
*p++ = c.centerX + v.dx1( id );
*p++ = c.centerY + v.dy1( id );
}
}
{
constexpr auto id = TopRight;
const auto& c = metrics.corner[ id ];
for ( ArcIterator it( c.stepCount, true ); !it.isDone(); ++it )
{
v.setAngle( it.cos(), it.sin() );
*p++ = c.centerX + v.dx1( id );
*p++ = c.centerY - v.dy1( id );
}
}
return path;
}

View File

@ -535,16 +535,22 @@ void QskBoxRenderer::renderRectBorder( const QRectF& rect,
void QskBoxRenderer::renderRectFill( const QRectF& rect,
const QskBoxBorderMetrics& border, QSGGeometry& geometry )
{
const Quad in = qskValidOrEmptyInnerRect( rect, border.widths() );
const Quad quad = qskValidOrEmptyInnerRect( rect, border.widths() );
if ( in.isEmpty() )
if ( quad.isEmpty() )
{
allocateLines< Line >( geometry, 0 );
geometry.allocate( 0 );
return;
}
const auto line = allocateLines< Line >( geometry, 2 );
qskCreateFillRandom( Qt::Vertical, in, ColorMapSolid(), line );
geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip );
geometry.allocate( 4 );
auto p = geometry.vertexDataAsPoint2D();
p[0].set( quad.left, quad.top );
p[1].set( quad.right, quad.top );
p[2].set( quad.left, quad.bottom );
p[3].set( quad.right, quad.bottom );
}
void QskBoxRenderer::renderRect( const QRectF& rect,
@ -664,18 +670,3 @@ void QskBoxRenderer::renderRectFill( const QskBoxRenderer::Quad& rect,
{
qskCreateFillOrdered( rect, gradient, line );
}
QVector< qreal > QskBoxRenderer::fillPathRect( const QRectF& rect,
const QskBoxBorderMetrics& border ) const
{
const auto r = border.adjustedRect( rect );
if ( r.isEmpty() )
return QVector< qreal >();
const qreal x1 = r.left();
const qreal x2 = r.right();
const qreal y1 = r.top();
const qreal y2 = r.bottom();
return { x1, y1, x2, y1, x2, y2, x1, y2, x1, y1 };
}