diff --git a/src/nodes/QskBoxRenderer.cpp b/src/nodes/QskBoxRenderer.cpp index aeb243b0..c3ee01b0 100644 --- a/src/nodes/QskBoxRenderer.cpp +++ b/src/nodes/QskBoxRenderer.cpp @@ -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; diff --git a/src/nodes/QskBoxRendererColorMap.h b/src/nodes/QskBoxRendererColorMap.h index 3a731b99..6de1eb2f 100644 --- a/src/nodes/QskBoxRendererColorMap.h +++ b/src/nodes/QskBoxRendererColorMap.h @@ -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; diff --git a/src/nodes/QskRectRenderer.cpp b/src/nodes/QskRectRenderer.cpp index 246b1e41..7cc5e302 100644 --- a/src/nodes/QskRectRenderer.cpp +++ b/src/nodes/QskRectRenderer.cpp @@ -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; diff --git a/src/nodes/QskRoundedRectRenderer.cpp b/src/nodes/QskRoundedRectRenderer.cpp index a1b5d4b7..e7256aa3 100644 --- a/src/nodes/QskRoundedRectRenderer.cpp +++ b/src/nodes/QskRoundedRectRenderer.cpp @@ -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,