more variations of gradient vectors supported
This commit is contained in:
parent
21e1206b2d
commit
f4aaf4cc92
@ -69,14 +69,16 @@ bool QskBoxRenderer::isGradientSupported(
|
||||
}
|
||||
case QskGradient::Linear:
|
||||
{
|
||||
const auto dir = gradient.linearDirection();
|
||||
if ( dir.x1() > dir.x2() || dir.y1() > dir.y2() )
|
||||
return false;
|
||||
|
||||
if ( shape.isRectangle() )
|
||||
{
|
||||
// rectangles are fully supported
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto dir = gradient.linearDirection();
|
||||
|
||||
if ( dir.isTilted() )
|
||||
{
|
||||
return ( dir.x1() == 0.0 && dir.x2() == 0.0 )
|
||||
@ -84,29 +86,31 @@ bool QskBoxRenderer::isGradientSupported(
|
||||
}
|
||||
else
|
||||
{
|
||||
// different radii at oppsoite corners are not implemented TODO ...
|
||||
|
||||
const auto r1 = shape.radius( Qt::TopLeftCorner );
|
||||
const auto r2 = shape.radius( Qt::TopRightCorner );
|
||||
const auto r3 = shape.radius( Qt::BottomLeftCorner );
|
||||
const auto r4 = shape.radius( Qt::BottomRightCorner );
|
||||
qreal r1, r2, r3, r4;
|
||||
|
||||
if ( dir.isHorizontal() )
|
||||
{
|
||||
if ( dir.x1() == 0.0 && dir.x2() == 1.0 )
|
||||
{
|
||||
return ( r1.width() == r3.width() )
|
||||
&& ( r2.width() == r4.width() );
|
||||
}
|
||||
r1 = shape.radius( Qt::TopLeftCorner ).width();
|
||||
r2 = shape.radius( Qt::BottomLeftCorner ).width();
|
||||
r3 = shape.radius( Qt::TopRightCorner ).width();
|
||||
r4 = shape.radius( Qt::BottomRightCorner ).width();
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( dir.y1() == 0.0 && dir.y2() == 1.0 )
|
||||
{
|
||||
return ( r1.height() == r2.height() )
|
||||
&& ( r3.height() == r4.height() );
|
||||
}
|
||||
r1 = shape.radius( Qt::TopLeftCorner ).height();
|
||||
r2 = shape.radius( Qt::TopRightCorner ).height();
|
||||
r3 = shape.radius( Qt::BottomLeftCorner ).height();
|
||||
r4 = shape.radius( Qt::BottomRightCorner ).height();
|
||||
}
|
||||
|
||||
if ( ( r1 <= 0.0 || r2 <= 0.0 ) && ( r3 <= 0.0 || r4 <= 0.0 ) )
|
||||
{
|
||||
// one of the corners is not rounded
|
||||
return true;
|
||||
}
|
||||
|
||||
// different radii at opposite corners are not implemented TODO ...
|
||||
return ( r1 == r2 ) && ( r3 == r4 );
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -217,15 +217,15 @@ namespace QskVertex
|
||||
ColoredLine* fillOrdered( ContourIterator& contourIt,
|
||||
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.
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
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;
|
||||
|
||||
|
@ -43,9 +43,6 @@ namespace
|
||||
|
||||
inline bool setGradientLine( qreal value, Color color, ColoredLine* line )
|
||||
{
|
||||
if ( value <= m_values[0] || value >= m_values[1] )
|
||||
return false;
|
||||
|
||||
const auto v = m_t + value * m_dt;
|
||||
|
||||
if ( m_vertical )
|
||||
@ -209,17 +206,17 @@ namespace
|
||||
{
|
||||
if( m_step == 0 || m_step == 3 )
|
||||
{
|
||||
const auto& p = m_corners[m_step].pos;
|
||||
const auto& p = m_corners[ m_step ].pos;
|
||||
line->setLine( p.x(), p.y(), p.x(), p.y(), color );
|
||||
}
|
||||
else
|
||||
{
|
||||
const qreal m = m_v.dy / m_v.dx;
|
||||
|
||||
auto p1 = m_corners[m_step - 1].pos;
|
||||
const auto& p2 = m_corners[m_step].pos;
|
||||
auto p1 = m_corners[ m_step - 1 ].pos;
|
||||
const auto& p2 = m_corners[ m_step ].pos;
|
||||
|
||||
if ( p1.x() == m_corners[m_step + 1].pos.x() )
|
||||
if ( p1.x() == m_corners[ m_step + 1 ].pos.x() )
|
||||
p1.ry() = p2.y() + ( p2.x() - p1.x() ) / m;
|
||||
else
|
||||
p1.rx() = p2.x() + ( p2.y() - p1.y() ) * m;
|
||||
|
@ -152,272 +152,198 @@ namespace
|
||||
Values m_outer[ 4 ];
|
||||
};
|
||||
|
||||
class FillValues
|
||||
class CornerValues
|
||||
{
|
||||
public:
|
||||
inline FillValues( const QskRoundedRectRenderer::Metrics& metrics )
|
||||
inline void setCorner( Qt::Orientation orientation, bool increasing,
|
||||
const QskRoundedRectRenderer::Metrics::Corner& c )
|
||||
{
|
||||
for ( int i = 0; i < 4; i++ )
|
||||
if ( orientation == Qt::Horizontal )
|
||||
{
|
||||
const auto& c = metrics.corner[ i ];
|
||||
auto& v = m_inner[ i ];
|
||||
|
||||
if ( c.radiusInnerX >= 0.0 )
|
||||
{
|
||||
v.x0 = 0.0;
|
||||
v.rx = c.radiusInnerX;
|
||||
}
|
||||
else
|
||||
{
|
||||
v.x0 = c.radiusInnerX;
|
||||
v.rx = 0.0;
|
||||
}
|
||||
|
||||
if ( c.radiusInnerY >= 0.0 )
|
||||
{
|
||||
v.y0 = 0.0;
|
||||
v.ry = c.radiusInnerY;
|
||||
}
|
||||
else
|
||||
{
|
||||
v.y0 = c.radiusInnerY;
|
||||
v.ry = 0.0;
|
||||
}
|
||||
m_center = c.centerX;
|
||||
m_radius = c.radiusInnerX;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_center = c.centerY;
|
||||
m_radius = c.radiusInnerY;
|
||||
}
|
||||
|
||||
const qreal f = increasing ? 1.0 : -1.0;
|
||||
|
||||
if ( m_radius < 0.0 )
|
||||
{
|
||||
m_center += m_radius * f;
|
||||
m_radius = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_radius *= f;
|
||||
}
|
||||
|
||||
stepCount = c.stepCount;
|
||||
}
|
||||
|
||||
inline void setAngle( qreal cos, qreal sin )
|
||||
qreal valueAt( qreal fv ) const
|
||||
{
|
||||
m_inner[ 0 ].setAngle( cos, sin );
|
||||
m_inner[ 1 ].setAngle( cos, sin );
|
||||
m_inner[ 2 ].setAngle( cos, sin );
|
||||
m_inner[ 3 ].setAngle( cos, sin );
|
||||
return m_center + fv * m_radius;
|
||||
}
|
||||
|
||||
inline qreal dx( int pos ) const { return m_inner[ pos ].dx; }
|
||||
inline qreal dy( int pos ) const { return m_inner[ pos ].dy; }
|
||||
int stepCount;
|
||||
|
||||
private:
|
||||
class Values
|
||||
{
|
||||
public:
|
||||
inline void setAngle( qreal cos, qreal sin )
|
||||
{
|
||||
dx = x0 + cos * rx;
|
||||
dy = y0 + sin * ry;
|
||||
}
|
||||
|
||||
qreal dx, dy;
|
||||
qreal x0, y0, rx, ry;
|
||||
};
|
||||
|
||||
Values m_inner[ 4 ];
|
||||
qreal m_center = 0.0;
|
||||
qreal m_radius = 0.0;
|
||||
};
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class VRectEllipseIterator
|
||||
class HVRectEllipseIterator
|
||||
{
|
||||
public:
|
||||
VRectEllipseIterator( const QskRoundedRectRenderer::Metrics& metrics )
|
||||
HVRectEllipseIterator(
|
||||
const QskRoundedRectRenderer::Metrics& metrics, const QLineF& vector )
|
||||
: m_metrics( metrics )
|
||||
, m_values( metrics )
|
||||
, m_vertical( vector.x1() == vector.x2() )
|
||||
{
|
||||
const auto& c = metrics.corner;
|
||||
|
||||
m_v[ 0 ].left = c[ TopLeft ].centerX;
|
||||
m_v[ 0 ].right = c[ TopRight ].centerX;
|
||||
m_v[ 0 ].y = metrics.innerQuad.top;
|
||||
auto v = m_values;
|
||||
|
||||
m_v[ 1 ] = m_v[ 0 ];
|
||||
if ( m_vertical )
|
||||
{
|
||||
v[0].setCorner( Qt::Horizontal, false, c[TopLeft] );
|
||||
v[1].setCorner( Qt::Horizontal, true, c[TopRight] );
|
||||
v[2].setCorner( Qt::Vertical, false, c[TopLeft] );
|
||||
|
||||
m_leadingCorner = ( c[ TopLeft ].stepCount >= c[ TopRight ].stepCount )
|
||||
? TopLeft : TopRight;
|
||||
v[3].setCorner( Qt::Horizontal, false, c[BottomLeft] );
|
||||
v[4].setCorner( Qt::Horizontal, true, c[BottomRight] );
|
||||
v[5].setCorner( Qt::Vertical, true, c[BottomLeft] );
|
||||
|
||||
m_arcIterator.reset( c[ m_leadingCorner ].stepCount, false );
|
||||
}
|
||||
m_pos0 = metrics.innerQuad.top;
|
||||
m_size = metrics.innerQuad.height;
|
||||
|
||||
inline bool setGradientLine( qreal value, Color color, ColoredLine* line )
|
||||
{
|
||||
const auto& q = m_metrics.innerQuad;
|
||||
const qreal y = q.top + value * q.height;
|
||||
m_t = m_pos0 + vector.y1() * m_size;
|
||||
m_dt = vector.dy() * m_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
v[0].setCorner( Qt::Vertical, false, c[TopLeft] );
|
||||
v[1].setCorner( Qt::Vertical, true, c[BottomLeft] );
|
||||
v[2].setCorner( Qt::Horizontal, false, c[TopLeft] );
|
||||
|
||||
const qreal f = ( y - m_v[ 0 ].y ) / ( m_v[ 1 ].y - m_v[ 0 ].y );
|
||||
const qreal left = m_v[ 0 ].left + f * ( m_v[ 1 ].left - m_v[ 0 ].left );
|
||||
const qreal right = m_v[ 0 ].right + f * ( m_v[ 1 ].right - m_v[ 0 ].right );
|
||||
v[3].setCorner( Qt::Vertical, false, c[TopRight] );
|
||||
v[4].setCorner( Qt::Vertical, true, c[BottomRight] );
|
||||
v[5].setCorner( Qt::Horizontal, true, c[TopRight] );
|
||||
|
||||
line->setLine( left, y, right, y, color );
|
||||
return true;
|
||||
}
|
||||
m_pos0 = metrics.innerQuad.left;
|
||||
m_size = metrics.innerQuad.width;
|
||||
|
||||
inline void setContourLine( Color color, ColoredLine* line )
|
||||
{
|
||||
line->setLine( m_v[ 1 ].left, m_v[ 1 ].y,
|
||||
m_v[ 1 ].right, m_v[ 1 ].y, color );
|
||||
}
|
||||
m_t = m_pos0 + vector.x1() * m_size;
|
||||
m_dt = vector.dx() * m_size;
|
||||
}
|
||||
|
||||
inline qreal value() const
|
||||
{
|
||||
const auto& q = m_metrics.innerQuad;
|
||||
return ( m_v[ 1 ].y - q.top ) / q.height;
|
||||
m_v1.from = v[0].valueAt( 1.0 );
|
||||
m_v1.to = v[1].valueAt( 1.0 );
|
||||
m_v1.pos = m_pos0;
|
||||
|
||||
m_v2 = m_v1;
|
||||
|
||||
const auto stepCount = qMax( v[0].stepCount, v[1].stepCount );
|
||||
m_arcIterator.reset( stepCount, false );
|
||||
}
|
||||
|
||||
inline bool advance()
|
||||
{
|
||||
const auto& centerQuad = m_metrics.centerQuad;
|
||||
const auto& c = m_metrics.corner;
|
||||
auto v = m_values;
|
||||
|
||||
if ( m_arcIterator.step() == m_arcIterator.stepCount() )
|
||||
{
|
||||
if ( m_arcIterator.isInverted() )
|
||||
return false;
|
||||
|
||||
m_leadingCorner = ( c[ BottomLeft ].stepCount >= c[ BottomRight ].stepCount )
|
||||
? BottomLeft : BottomRight;
|
||||
|
||||
m_arcIterator.reset( c[ m_leadingCorner ].stepCount, true );
|
||||
|
||||
if ( centerQuad.top < centerQuad.bottom )
|
||||
{
|
||||
m_v[ 0 ] = m_v[ 1 ];
|
||||
// we have finished the closing "corners"
|
||||
return false;
|
||||
}
|
||||
|
||||
m_v[ 1 ].left = m_metrics.innerQuad.left;
|
||||
m_v[ 1 ].right = m_metrics.innerQuad.right;
|
||||
m_v[ 1 ].y = centerQuad.bottom;
|
||||
const auto stepCount = qMax( v[3].stepCount, v[4].stepCount );
|
||||
m_arcIterator.reset( stepCount, true );
|
||||
|
||||
const qreal pos1 = v[2].valueAt( 0.0 );
|
||||
const qreal pos2 = v[5].valueAt( 0.0 );
|
||||
|
||||
if ( pos1 < pos2 )
|
||||
{
|
||||
// the real rectangle - between the rounded "corners "
|
||||
m_v1 = m_v2;
|
||||
|
||||
m_v2.from = v[3].valueAt( 1.0 );
|
||||
m_v2.to = v[4].valueAt( 1.0 );
|
||||
m_v2.pos = pos2;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
m_arcIterator.increment();
|
||||
m_values.setAngle( m_arcIterator.cos(), m_arcIterator.sin() );
|
||||
|
||||
m_v[ 0 ] = m_v[ 1 ];
|
||||
m_v1 = m_v2;
|
||||
|
||||
if ( m_arcIterator.isInverted() )
|
||||
{
|
||||
m_v[ 1 ].left = c[ BottomLeft ].centerX - m_values.dx( BottomLeft );
|
||||
m_v[ 1 ].right = c[ BottomRight ].centerX + m_values.dx( BottomRight );
|
||||
m_v[ 1 ].y = c[ m_leadingCorner ].centerY + m_values.dy( m_leadingCorner );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_v[ 1 ].left = c[ TopLeft ].centerX - m_values.dx( TopLeft );
|
||||
m_v[ 1 ].right = c[ TopRight ].centerX + m_values.dx( TopRight );
|
||||
m_v[ 1 ].y = c[ m_leadingCorner ].centerY - m_values.dy( m_leadingCorner );
|
||||
}
|
||||
v += 3;
|
||||
|
||||
m_v2.from = v[0].valueAt( m_arcIterator.cos() );
|
||||
m_v2.to = v[1].valueAt( m_arcIterator.cos() );
|
||||
m_v2.pos = v[2].valueAt( m_arcIterator.sin() );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const QskRoundedRectRenderer::Metrics& m_metrics;
|
||||
ArcIterator m_arcIterator;
|
||||
int m_leadingCorner;
|
||||
FillValues m_values;
|
||||
struct { qreal left, right, y; } m_v[ 2 ];
|
||||
};
|
||||
|
||||
class HRectEllipseIterator
|
||||
{
|
||||
public:
|
||||
HRectEllipseIterator( const QskRoundedRectRenderer::Metrics& metrics )
|
||||
: m_metrics( metrics )
|
||||
, m_values( metrics )
|
||||
{
|
||||
const auto& c = metrics.corner;
|
||||
|
||||
m_v[ 0 ].top = c[ TopLeft ].centerY;
|
||||
m_v[ 0 ].bottom = c[ BottomLeft ].centerY;
|
||||
m_v[ 0 ].x = metrics.innerQuad.left;
|
||||
|
||||
m_v[ 1 ] = m_v[ 0 ];
|
||||
|
||||
m_leadingCorner = ( c[ TopLeft ].stepCount >= c[ BottomLeft ].stepCount )
|
||||
? TopLeft : BottomLeft;
|
||||
|
||||
m_arcIterator.reset( c[ m_leadingCorner ].stepCount, true );
|
||||
}
|
||||
|
||||
inline bool setGradientLine( qreal value, Color color, ColoredLine* line )
|
||||
{
|
||||
const auto& q = m_metrics.innerQuad;
|
||||
const qreal x = q.left + value * q.width;
|
||||
const auto pos = m_t + value * m_dt;
|
||||
|
||||
const qreal f = ( x - m_v[ 0 ].x ) / ( m_v[ 1 ].x - m_v[ 0 ].x );
|
||||
const qreal top = m_v[ 0 ].top + f * ( m_v[ 1 ].top - m_v[ 0 ].top );
|
||||
const qreal bottom = m_v[ 0 ].bottom + f * ( m_v[ 1 ].bottom - m_v[ 0 ].bottom );
|
||||
const qreal f = ( pos - m_v1.pos ) / ( m_v2.pos - m_v1.pos );
|
||||
|
||||
line->setLine( x, top, x, bottom, color );
|
||||
const qreal v1 = m_v1.from + f * ( m_v2.from - m_v1.from );
|
||||
const qreal v2 = m_v1.to + f * ( m_v2.to - m_v1.to );
|
||||
|
||||
setLine( v1, v2, pos, color, line );
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void setContourLine( Color color, ColoredLine* line )
|
||||
{
|
||||
line->setLine( m_v[ 1 ].x, m_v[ 1 ].top,
|
||||
m_v[ 1 ].x, m_v[ 1 ].bottom, color );
|
||||
setLine( m_v2.from, m_v2.to, m_v2.pos, color, line );
|
||||
}
|
||||
|
||||
inline qreal value() const
|
||||
{
|
||||
const auto& q = m_metrics.innerQuad;
|
||||
return ( m_v[ 1 ].x - q.left ) / q.width;
|
||||
}
|
||||
|
||||
inline bool advance()
|
||||
{
|
||||
const auto& centerQuad = m_metrics.centerQuad;
|
||||
const auto& c = m_metrics.corner;
|
||||
|
||||
if ( m_arcIterator.step() == m_arcIterator.stepCount() )
|
||||
{
|
||||
if ( !m_arcIterator.isInverted() )
|
||||
return false;
|
||||
|
||||
m_leadingCorner = ( c[ TopRight ].stepCount >= c[ BottomRight ].stepCount )
|
||||
? TopRight : BottomRight;
|
||||
|
||||
m_arcIterator.reset( c[ m_leadingCorner ].stepCount, false );
|
||||
if ( centerQuad.left < centerQuad.right )
|
||||
{
|
||||
m_v[ 0 ] = m_v[ 1 ];
|
||||
|
||||
m_v[ 1 ].top = m_metrics.innerQuad.top;
|
||||
m_v[ 1 ].bottom = m_metrics.innerQuad.bottom;
|
||||
m_v[ 1 ].x = centerQuad.right;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
m_arcIterator.increment();
|
||||
m_values.setAngle( m_arcIterator.cos(), m_arcIterator.sin() );
|
||||
|
||||
m_v[ 0 ] = m_v[ 1 ];
|
||||
|
||||
if ( m_arcIterator.isInverted() )
|
||||
{
|
||||
m_v[ 1 ].top = c[ TopLeft ].centerY - m_values.dy( TopLeft );
|
||||
m_v[ 1 ].bottom = c[ BottomLeft ].centerY + m_values.dy( BottomLeft );
|
||||
m_v[ 1 ].x = c[ m_leadingCorner ].centerX - m_values.dx( m_leadingCorner );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_v[ 1 ].top = c[ TopRight ].centerY - m_values.dy( TopRight );
|
||||
m_v[ 1 ].bottom = c[ BottomRight ].centerY + m_values.dy( BottomRight );
|
||||
m_v[ 1 ].x = c[ m_leadingCorner ].centerX + m_values.dx( m_leadingCorner );
|
||||
}
|
||||
|
||||
return true;
|
||||
// coordinate translated into the gradiet vector
|
||||
return ( m_v2.pos - m_t ) / m_dt;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
inline void setLine( qreal from, qreal to, qreal pos,
|
||||
Color color, ColoredLine* line )
|
||||
{
|
||||
if ( m_vertical )
|
||||
line->setLine( from, pos, to, pos, color );
|
||||
else
|
||||
line->setLine( pos, from, pos, to, color );
|
||||
}
|
||||
|
||||
const QskRoundedRectRenderer::Metrics& m_metrics;
|
||||
const bool m_vertical;
|
||||
|
||||
ArcIterator m_arcIterator;
|
||||
int m_leadingCorner;
|
||||
FillValues m_values;
|
||||
struct { qreal top, bottom, x; } m_v[ 2 ];
|
||||
|
||||
CornerValues m_values[6];
|
||||
struct { qreal from, to, pos; } m_v1, m_v2;
|
||||
|
||||
qreal m_pos0, m_size;
|
||||
qreal m_t, m_dt; // to translate into gradient values
|
||||
};
|
||||
}
|
||||
|
||||
@ -1045,16 +971,10 @@ static inline void qskRenderFillOrdered(
|
||||
implemented TODO ...
|
||||
*/
|
||||
|
||||
if ( gradient.linearDirection().isHorizontal() )
|
||||
{
|
||||
HRectEllipseIterator it( metrics );
|
||||
QskVertex::fillOrdered( it, gradient, lineCount, lines );
|
||||
}
|
||||
else
|
||||
{
|
||||
VRectEllipseIterator it( metrics );
|
||||
QskVertex::fillOrdered( it, gradient, lineCount, lines );
|
||||
}
|
||||
const auto dir = gradient.linearDirection();
|
||||
|
||||
HVRectEllipseIterator it( metrics, dir.vector() );
|
||||
QskVertex::fillOrdered( it, gradient, lineCount,lines );
|
||||
}
|
||||
|
||||
QskRoundedRectRenderer::Metrics::Metrics( const QRectF& rect,
|
||||
|
Loading…
x
Reference in New Issue
Block a user