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;
};
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 >
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
{
while ( !colorIt.isDone() && ( colorIt.value() < contourIt.value() ) )
{
if ( contourIt.setGradientLine( colorIt.value(), colorIt.color(), line ) )
line++;
const auto value = colorIt.value();
/*
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();
}
const auto color = colorIt.colorAt( contourIt.value() );
contourIt.setContourLine( color, line++ );
contourIt.setContourLine( color, l++ );
} 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 >
ColoredLine* fillOrdered( ContourIterator& contourIt,
const QskGradient& gradient, ColoredLine* line )
const QskGradient& gradient, int lineCount, ColoredLine* lines )
{
if ( gradient.stepCount() == 1 )
{
SimpleColorIterator colorIt( gradient.rgbStart(), gradient.rgbEnd() );
line = fillOrdered( contourIt, colorIt, line );
lines = fillOrdered( contourIt, colorIt, lineCount, lines );
}
else
{
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 );
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

View File

@ -132,9 +132,6 @@ namespace
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 x = m_v.x + m_v.dx * value;
@ -253,19 +250,19 @@ namespace
}
static ColoredLine* qskAddFillLines( const Quad& rect,
const QskGradient& gradient, ColoredLine* line )
const QskGradient& gradient, int lineCount, ColoredLine* line )
{
const auto dir = gradient.linearDirection();
if ( dir.isTilted() )
{
DRectIterator it( rect, dir.vector() );
line = QskVertex::fillOrdered( it, gradient, line );
line = QskVertex::fillOrdered( it, gradient, lineCount, line );
}
else
{
HVRectIterator it( rect, dir.vector() );
line = QskVertex::fillOrdered( it, gradient, line );
line = QskVertex::fillOrdered( it, gradient, lineCount, line );
}
return line;
@ -410,9 +407,9 @@ void QskRectRenderer::renderBorder( const QRectF& 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,
@ -477,10 +474,10 @@ void QskRectRenderer::renderRect( const QRectF& rect,
}
}
const auto line = allocateLines< ColoredLine >(
const auto lines = allocateLines< ColoredLine >(
geometry, borderLineCount + fillLineCount );
auto l = line;
auto l = lines;
if ( fillLineCount > 0 )
{
@ -493,12 +490,14 @@ void QskRectRenderer::renderRect( const QRectF& rect,
}
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 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.bottom(), c2 );
@ -511,27 +510,11 @@ void QskRectRenderer::renderRect( const QRectF& rect,
}
else
{
l = qskAddFillLines( in, gradient, l );
l = qskAddFillLines( in, gradient, fillLineCount, l );
}
}
if ( l < line + 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 );
Q_ASSERT( l - lines == fillLineCount );
}
if ( borderLineCount > 0 )
@ -546,6 +529,6 @@ void QskRectRenderer::renderRect( const QRectF& rect,
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
{
void renderFill0( const QskVertex::Quad&,
const QskGradient&, QskVertex::ColoredLine* );
const QskGradient&, int lineCount, QskVertex::ColoredLine* );
}
#endif

View File

@ -27,78 +27,6 @@ namespace
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 )
{
return qMax( 0, gradient.stepCount() - 1 );
@ -205,7 +133,8 @@ namespace
{ return m_uniform ? m_outer[ 0 ].dy : m_outer[ pos ].dy; }
private:
bool m_uniform;
const bool m_uniform;
class Values
{
public:
@ -882,8 +811,6 @@ static inline Qt::Orientation qskQtOrientation( const QskGradient& gradient )
static inline int qskFillLineCount(
const QskRoundedRectRenderer::Metrics& metrics, const QskGradient& gradient )
{
const int stepCount = metrics.corner[ 0 ].stepCount;
if ( !gradient.isVisible() )
return 0;
@ -915,7 +842,7 @@ static inline int qskFillLineCount(
}
else
{
lineCount += 2 * ( stepCount + 1 );
lineCount += 2 * ( metrics.corner[ 0 ].stepCount + 1 );
if ( metrics.centerQuad.left >= metrics.centerQuad.right )
lineCount--;
@ -1111,7 +1038,7 @@ static inline void qskRenderBoxRandom(
static inline void qskRenderFillOrdered(
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
@ -1121,12 +1048,12 @@ static inline void qskRenderFillOrdered(
if ( gradient.linearDirection().isHorizontal() )
{
HRectEllipseIterator it( metrics );
QskVertex::fillOrdered( it, gradient, lines );
QskVertex::fillOrdered( it, gradient, lineCount, lines );
}
else
{
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 )
{
const Metrics metrics( rect, shape, border );
const auto isTilted = gradient.linearDirection().isTilted();
int fillLineCount = 0;
@ -1446,7 +1374,7 @@ void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect,
fillLineCount = gradient.stepCount() + 1;
#if 1
if ( gradient.linearDirection().isTilted() )
if ( isTilted )
{
if ( metrics.centerQuad.width == metrics.centerQuad.height )
{
@ -1489,7 +1417,7 @@ void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect,
bool extraLine = false;
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
@ -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;
if ( fillLineCount > 0 )
@ -1512,7 +1440,7 @@ void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect,
}
else if ( !gradient.isMonochrome() )
{
if ( gradient.stepCount() > 1 || gradient.linearDirection().isTilted() )
if ( gradient.stepCount() > 1 || isTilted )
fillRandom = false;
}
@ -1534,24 +1462,25 @@ void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect,
if ( fillRandom )
{
qskRenderBoxRandom( metrics, borderColors,
gradient, line, line + fillLineCount );
gradient, lines, lines + fillLineCount );
}
else
{
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
{
qskRenderFillOrdered( metrics, gradient, line );
qskRenderFillOrdered( metrics, gradient, fillLineCount, lines );
}
auto borderLines = line + fillLineCount;
auto borderLines = lines + fillLineCount;
if ( extraLine )
borderLines++;
@ -1560,7 +1489,7 @@ void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect,
if ( extraLine )
{
const auto l = line + fillLineCount;
const auto l = lines + fillLineCount;
l[ 0 ].p1 = l[ -1 ].p2;
l[ 0 ].p2 = l[ 1 ].p1;
}
@ -1570,21 +1499,22 @@ void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect,
{
if ( fillRandom )
{
qskRenderFillRandom( metrics, gradient, line );
qskRenderFillRandom( metrics, gradient, lines );
}
else
{
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
{
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 ...
*/
#endif
qskRenderBorder( metrics, Qt::Vertical, borderColors, line );
qskRenderBorder( metrics, Qt::Vertical, borderColors, lines );
}
}

View File

@ -10,6 +10,7 @@
#include <qcolor.h>
#include <qsggeometry.h>
#include <qmath.h>
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
{
void debugGeometry( const QSGGeometry& );