QskBoxRenderer improvements

This commit is contained in:
Uwe Rathmann 2023-01-05 13:06:32 +01:00
parent 4bd294f72c
commit 21e1206b2d
6 changed files with 160 additions and 138 deletions

View File

@ -205,44 +205,77 @@ namespace QskVertex
Color m_color1, m_color2; Color m_color1, m_color2;
}; };
inline ColoredLine* fillUp( ColoredLine* lines, const ColoredLine& l, int count )
{
for ( int i = 0; i < count; i++ )
*lines++ = l;
return lines;
}
template< class ContourIterator, class ColorIterator > template< class ContourIterator, class ColorIterator >
ColoredLine* fillOrdered( ContourIterator& contourIt, ColoredLine* fillOrdered( ContourIterator& contourIt,
ColorIterator& colorIt, ColoredLine* line ) ColorIterator& colorIt, int lineCount, ColoredLine* lines )
{ {
/*
When the the vector exceeds [ 0.0, 1.0 ] we might have
gradient lines lying outside the contour.
This effect could be precalculated - however we might end
up difficult code with potential bugs.
So we allow the allocation code to ignore the effect by
adding duplicates of the last line.
*/
const auto value0 = contourIt.value();
ColoredLine* l = lines;
do do
{ {
while ( !colorIt.isDone() && ( colorIt.value() < contourIt.value() ) ) while ( !colorIt.isDone() && ( colorIt.value() < contourIt.value() ) )
{ {
if ( contourIt.setGradientLine( colorIt.value(), colorIt.color(), line ) ) const auto value = colorIt.value();
line++;
/*
When having a gradient vector below 0.0 we
will have gradient lines outside of the contour
*/
if ( value > value0 )
contourIt.setGradientLine( value, colorIt.color(), l++ );
colorIt.advance(); colorIt.advance();
} }
const auto color = colorIt.colorAt( contourIt.value() ); const auto color = colorIt.colorAt( contourIt.value() );
contourIt.setContourLine( color, line++ ); contourIt.setContourLine( color, l++ );
} while ( contourIt.advance() ); } while ( contourIt.advance() );
return line; if ( lineCount >= 0 )
{
if ( const auto count = lineCount - ( l - lines ) )
l = QskVertex::fillUp( l, *( l - 1 ), count );
}
return l;
} }
template< class ContourIterator > template< class ContourIterator >
ColoredLine* fillOrdered( ContourIterator& contourIt, ColoredLine* fillOrdered( ContourIterator& contourIt,
const QskGradient& gradient, ColoredLine* line ) const QskGradient& gradient, int lineCount, ColoredLine* lines )
{ {
if ( gradient.stepCount() == 1 ) if ( gradient.stepCount() == 1 )
{ {
SimpleColorIterator colorIt( gradient.rgbStart(), gradient.rgbEnd() ); SimpleColorIterator colorIt( gradient.rgbStart(), gradient.rgbEnd() );
line = fillOrdered( contourIt, colorIt, line ); lines = fillOrdered( contourIt, colorIt, lineCount, lines );
} }
else else
{ {
GradientColorIterator colorIt( gradient.stops() ); GradientColorIterator colorIt( gradient.stops() );
line = fillOrdered( contourIt, colorIt, line ); lines = fillOrdered( contourIt, colorIt, lineCount, lines );
} }
return line; return lines;
} }
} }

View File

@ -610,7 +610,7 @@ void QskRoundedRectRenderer::renderDiagonalFill( const QskRoundedRectRenderer::M
const ValueCurve curve( metrics ); const ValueCurve curve( metrics );
DRectellipseIterator it( metrics, curve ); DRectellipseIterator it( metrics, curve );
auto line = QskVertex::fillOrdered( it, gradient, lines ); auto line = QskVertex::fillOrdered( it, gradient, -1, lines );
/* /*
There are a couple of reasons, why less points have been rendered There are a couple of reasons, why less points have been rendered

View File

@ -132,9 +132,6 @@ namespace
inline bool setGradientLine( qreal value, Color color, ColoredLine* line ) inline bool setGradientLine( qreal value, Color color, ColoredLine* line )
{ {
if ( value <= m_corners[0].value || value >= m_corners[3].value )
return false;
const qreal m = m_v.dy / m_v.dx; const qreal m = m_v.dy / m_v.dx;
const qreal x = m_v.x + m_v.dx * value; const qreal x = m_v.x + m_v.dx * value;
@ -253,19 +250,19 @@ namespace
} }
static ColoredLine* qskAddFillLines( const Quad& rect, static ColoredLine* qskAddFillLines( const Quad& rect,
const QskGradient& gradient, ColoredLine* line ) const QskGradient& gradient, int lineCount, ColoredLine* line )
{ {
const auto dir = gradient.linearDirection(); const auto dir = gradient.linearDirection();
if ( dir.isTilted() ) if ( dir.isTilted() )
{ {
DRectIterator it( rect, dir.vector() ); DRectIterator it( rect, dir.vector() );
line = QskVertex::fillOrdered( it, gradient, line ); line = QskVertex::fillOrdered( it, gradient, lineCount, line );
} }
else else
{ {
HVRectIterator it( rect, dir.vector() ); HVRectIterator it( rect, dir.vector() );
line = QskVertex::fillOrdered( it, gradient, line ); line = QskVertex::fillOrdered( it, gradient, lineCount, line );
} }
return line; return line;
@ -410,9 +407,9 @@ void QskRectRenderer::renderBorder( const QRectF& rect,
} }
void QskRectRenderer::renderFill0( const QskVertex::Quad& rect, void QskRectRenderer::renderFill0( const QskVertex::Quad& rect,
const QskGradient& gradient, QskVertex::ColoredLine* line ) const QskGradient& gradient, int lineCount, QskVertex::ColoredLine* line )
{ {
qskAddFillLines( rect, gradient, line ); qskAddFillLines( rect, gradient, lineCount, line );
} }
void QskRectRenderer::renderFill( const QRectF& rect, void QskRectRenderer::renderFill( const QRectF& rect,
@ -477,10 +474,10 @@ void QskRectRenderer::renderRect( const QRectF& rect,
} }
} }
const auto line = allocateLines< ColoredLine >( const auto lines = allocateLines< ColoredLine >(
geometry, borderLineCount + fillLineCount ); geometry, borderLineCount + fillLineCount );
auto l = line; auto l = lines;
if ( fillLineCount > 0 ) if ( fillLineCount > 0 )
{ {
@ -493,12 +490,14 @@ void QskRectRenderer::renderRect( const QRectF& rect,
} }
else else
{ {
if ( gradient.stepCount() <= 1 && !gradient.linearDirection().isTilted() ) const auto dir = gradient.linearDirection();
if ( gradient.stepCount() <= 1 && !dir.isTilted() )
{ {
const auto c1 = gradient.rgbStart(); const auto c1 = gradient.rgbStart();
const auto c2 = gradient.rgbEnd(); const auto c2 = gradient.rgbEnd();
if ( gradient.linearDirection().isVertical() ) if ( dir.isVertical() )
{ {
( l++ )->setHLine( rect.left(), rect.right(), rect.top(), c1 ); ( l++ )->setHLine( rect.left(), rect.right(), rect.top(), c1 );
( l++ )->setHLine( rect.left(), rect.right(), rect.bottom(), c2 ); ( l++ )->setHLine( rect.left(), rect.right(), rect.bottom(), c2 );
@ -511,27 +510,11 @@ void QskRectRenderer::renderRect( const QRectF& rect,
} }
else else
{ {
l = qskAddFillLines( in, gradient, l ); l = qskAddFillLines( in, gradient, fillLineCount, l );
} }
} }
if ( l < line + fillLineCount ) Q_ASSERT( l - lines == fillLineCount );
{
/*
When the the vector exceeds [ 0.0, 1.0 ] we might have
gradient lines lying outside the rectangle.
Precalculating this effect would save some memory - however
these corner cases are not worth to make the implementation
even more complicated.
So let's fill the memory with duplicates of the final
contour line instead.
*/
const auto& llast = *( l - 1 );
while ( l < line + fillLineCount )
*l++ = llast;
}
Q_ASSERT( l - line == fillLineCount );
} }
if ( borderLineCount > 0 ) if ( borderLineCount > 0 )
@ -546,6 +529,6 @@ void QskRectRenderer::renderRect( const QRectF& rect,
l = qskAddBorderLines( rect, in, borderColors, l ); l = qskAddBorderLines( rect, in, borderColors, l );
} }
Q_ASSERT( l - line == borderLineCount + fillLineCount ); Q_ASSERT( l - lines == borderLineCount + fillLineCount );
} }
} }

View File

@ -39,7 +39,7 @@ namespace QskVertex
namespace QskRectRenderer namespace QskRectRenderer
{ {
void renderFill0( const QskVertex::Quad&, void renderFill0( const QskVertex::Quad&,
const QskGradient&, QskVertex::ColoredLine* ); const QskGradient&, int lineCount, QskVertex::ColoredLine* );
} }
#endif #endif

View File

@ -27,78 +27,6 @@ namespace
BottomRight = Qt::BottomRightCorner BottomRight = Qt::BottomRightCorner
}; };
class ArcIterator
{
public:
inline ArcIterator() = default;
inline ArcIterator( int stepCount, bool inverted = false )
{
reset( stepCount, inverted );
}
void reset( int stepCount, bool inverted )
{
m_inverted = inverted;
if ( inverted )
{
m_cos = 1.0;
m_sin = 0.0;
}
else
{
m_cos = 0.0;
m_sin = 1.0;
}
m_stepIndex = 0;
m_stepCount = stepCount;
const double angleStep = M_PI_2 / stepCount;
m_cosStep = qFastCos( angleStep );
m_sinStep = qFastSin( angleStep );
}
inline bool isInverted() const { return m_inverted; }
inline double cos() const { return m_cos; }
inline double sin() const { return m_inverted ? -m_sin : 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; }
inline void increment()
{
const double cos0 = m_cos;
m_cos = m_cos * m_cosStep + m_sin * m_sinStep;
m_sin = m_sin * m_cosStep - cos0 * m_sinStep;
++m_stepIndex;
}
inline void operator++() { increment(); }
static int segmentHint( double radius )
{
const double arcLength = radius * M_PI_2;
return qBound( 3, qCeil( arcLength / 3.0 ), 18 ); // every 3 pixels
}
private:
double m_cos;
double m_sin;
int m_stepIndex;
double m_cosStep;
double m_sinStep;
int m_stepCount;
bool m_inverted;
};
int additionalGradientStops( const QskGradient& gradient ) int additionalGradientStops( const QskGradient& gradient )
{ {
return qMax( 0, gradient.stepCount() - 1 ); return qMax( 0, gradient.stepCount() - 1 );
@ -205,7 +133,8 @@ namespace
{ return m_uniform ? m_outer[ 0 ].dy : m_outer[ pos ].dy; } { return m_uniform ? m_outer[ 0 ].dy : m_outer[ pos ].dy; }
private: private:
bool m_uniform; const bool m_uniform;
class Values class Values
{ {
public: public:
@ -882,8 +811,6 @@ static inline Qt::Orientation qskQtOrientation( const QskGradient& gradient )
static inline int qskFillLineCount( static inline int qskFillLineCount(
const QskRoundedRectRenderer::Metrics& metrics, const QskGradient& gradient ) const QskRoundedRectRenderer::Metrics& metrics, const QskGradient& gradient )
{ {
const int stepCount = metrics.corner[ 0 ].stepCount;
if ( !gradient.isVisible() ) if ( !gradient.isVisible() )
return 0; return 0;
@ -915,7 +842,7 @@ static inline int qskFillLineCount(
} }
else else
{ {
lineCount += 2 * ( stepCount + 1 ); lineCount += 2 * ( metrics.corner[ 0 ].stepCount + 1 );
if ( metrics.centerQuad.left >= metrics.centerQuad.right ) if ( metrics.centerQuad.left >= metrics.centerQuad.right )
lineCount--; lineCount--;
@ -1111,7 +1038,7 @@ static inline void qskRenderBoxRandom(
static inline void qskRenderFillOrdered( static inline void qskRenderFillOrdered(
const QskRoundedRectRenderer::Metrics& metrics, const QskRoundedRectRenderer::Metrics& metrics,
const QskGradient& gradient, ColoredLine* lines ) const QskGradient& gradient, int lineCount, ColoredLine* lines )
{ {
/* /*
The algo for irregular radii at opposite corners is not yet The algo for irregular radii at opposite corners is not yet
@ -1121,12 +1048,12 @@ static inline void qskRenderFillOrdered(
if ( gradient.linearDirection().isHorizontal() ) if ( gradient.linearDirection().isHorizontal() )
{ {
HRectEllipseIterator it( metrics ); HRectEllipseIterator it( metrics );
QskVertex::fillOrdered( it, gradient, lines ); QskVertex::fillOrdered( it, gradient, lineCount, lines );
} }
else else
{ {
VRectEllipseIterator it( metrics ); VRectEllipseIterator it( metrics );
QskVertex::fillOrdered( it, gradient, lines ); QskVertex::fillOrdered( it, gradient, lineCount, lines );
} }
} }
@ -1434,6 +1361,7 @@ void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect,
QSGGeometry& geometry ) QSGGeometry& geometry )
{ {
const Metrics metrics( rect, shape, border ); const Metrics metrics( rect, shape, border );
const auto isTilted = gradient.linearDirection().isTilted();
int fillLineCount = 0; int fillLineCount = 0;
@ -1446,7 +1374,7 @@ void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect,
fillLineCount = gradient.stepCount() + 1; fillLineCount = gradient.stepCount() + 1;
#if 1 #if 1
if ( gradient.linearDirection().isTilted() ) if ( isTilted )
{ {
if ( metrics.centerQuad.width == metrics.centerQuad.height ) if ( metrics.centerQuad.width == metrics.centerQuad.height )
{ {
@ -1489,7 +1417,7 @@ void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect,
bool extraLine = false; bool extraLine = false;
if ( borderLineCount > 0 && fillLineCount > 0 ) if ( borderLineCount > 0 && fillLineCount > 0 )
{ {
if ( !gradient.isMonochrome() && gradient.linearDirection().isTilted() ) if ( !gradient.isMonochrome() && isTilted )
{ {
/* /*
The filling ends at 45° and we have no implementation The filling ends at 45° and we have no implementation
@ -1501,7 +1429,7 @@ void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect,
} }
} }
auto line = allocateLines< ColoredLine >( geometry, lineCount ); auto lines = allocateLines< ColoredLine >( geometry, lineCount );
bool fillRandom = true; bool fillRandom = true;
if ( fillLineCount > 0 ) if ( fillLineCount > 0 )
@ -1512,7 +1440,7 @@ void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect,
} }
else if ( !gradient.isMonochrome() ) else if ( !gradient.isMonochrome() )
{ {
if ( gradient.stepCount() > 1 || gradient.linearDirection().isTilted() ) if ( gradient.stepCount() > 1 || isTilted )
fillRandom = false; fillRandom = false;
} }
@ -1534,24 +1462,25 @@ void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect,
if ( fillRandom ) if ( fillRandom )
{ {
qskRenderBoxRandom( metrics, borderColors, qskRenderBoxRandom( metrics, borderColors,
gradient, line, line + fillLineCount ); gradient, lines, lines + fillLineCount );
} }
else else
{ {
if ( metrics.isTotallyCropped ) if ( metrics.isTotallyCropped )
{ {
QskRectRenderer::renderFill0( metrics.innerQuad, gradient, line ); QskRectRenderer::renderFill0( metrics.innerQuad,
gradient, fillLineCount, lines );
} }
else if ( gradient.linearDirection().isTilted() ) else if ( isTilted )
{ {
renderDiagonalFill( metrics, gradient, fillLineCount, line ); renderDiagonalFill( metrics, gradient, fillLineCount, lines );
} }
else else
{ {
qskRenderFillOrdered( metrics, gradient, line ); qskRenderFillOrdered( metrics, gradient, fillLineCount, lines );
} }
auto borderLines = line + fillLineCount; auto borderLines = lines + fillLineCount;
if ( extraLine ) if ( extraLine )
borderLines++; borderLines++;
@ -1560,7 +1489,7 @@ void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect,
if ( extraLine ) if ( extraLine )
{ {
const auto l = line + fillLineCount; const auto l = lines + fillLineCount;
l[ 0 ].p1 = l[ -1 ].p2; l[ 0 ].p1 = l[ -1 ].p2;
l[ 0 ].p2 = l[ 1 ].p1; l[ 0 ].p2 = l[ 1 ].p1;
} }
@ -1570,21 +1499,22 @@ void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect,
{ {
if ( fillRandom ) if ( fillRandom )
{ {
qskRenderFillRandom( metrics, gradient, line ); qskRenderFillRandom( metrics, gradient, lines );
} }
else else
{ {
if ( metrics.isTotallyCropped ) if ( metrics.isTotallyCropped )
{ {
QskRectRenderer::renderFill0( metrics.innerQuad, gradient, line ); QskRectRenderer::renderFill0( metrics.innerQuad,
gradient, fillLineCount, lines );
} }
else if ( gradient.linearDirection().isTilted() ) else if ( isTilted )
{ {
renderDiagonalFill( metrics, gradient, fillLineCount, line ); renderDiagonalFill( metrics, gradient, fillLineCount, lines );
} }
else else
{ {
qskRenderFillOrdered( metrics, gradient, line ); qskRenderFillOrdered( metrics, gradient, fillLineCount, lines );
} }
} }
} }
@ -1596,6 +1526,6 @@ void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect,
border colors, we could treat it like filling without border. TODO ... border colors, we could treat it like filling without border. TODO ...
*/ */
#endif #endif
qskRenderBorder( metrics, Qt::Vertical, borderColors, line ); qskRenderBorder( metrics, Qt::Vertical, borderColors, lines );
} }
} }

View File

@ -10,6 +10,7 @@
#include <qcolor.h> #include <qcolor.h>
#include <qsggeometry.h> #include <qsggeometry.h>
#include <qmath.h>
namespace QskVertex namespace QskVertex
{ {
@ -213,6 +214,81 @@ namespace QskVertex
}; };
} }
namespace
{
class ArcIterator
{
public:
inline ArcIterator() = default;
inline ArcIterator( int stepCount, bool inverted = false )
{
reset( stepCount, inverted );
}
void reset( int stepCount, bool inverted )
{
m_inverted = inverted;
if ( inverted )
{
m_cos = 1.0;
m_sin = 0.0;
}
else
{
m_cos = 0.0;
m_sin = 1.0;
}
m_stepIndex = 0;
m_stepCount = stepCount;
const double angleStep = M_PI_2 / stepCount;
m_cosStep = qFastCos( angleStep );
m_sinStep = qFastSin( angleStep );
}
inline bool isInverted() const { return m_inverted; }
inline double cos() const { return m_cos; }
inline double sin() const { return m_inverted ? -m_sin : 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; }
inline void increment()
{
const double cos0 = m_cos;
m_cos = m_cos * m_cosStep + m_sin * m_sinStep;
m_sin = m_sin * m_cosStep - cos0 * m_sinStep;
++m_stepIndex;
}
inline void operator++() { increment(); }
static int segmentHint( double radius )
{
const double arcLength = radius * M_PI_2;
return qBound( 3, qCeil( arcLength / 3.0 ), 18 ); // every 3 pixels
}
private:
double m_cos;
double m_sin;
int m_stepIndex;
double m_cosStep;
double m_sinStep;
int m_stepCount;
bool m_inverted;
};
}
namespace QskVertex namespace QskVertex
{ {
void debugGeometry( const QSGGeometry& ); void debugGeometry( const QSGGeometry& );