diff --git a/playground/gradients/main.cpp b/playground/gradients/main.cpp index e9932eb9..3d730f43 100644 --- a/playground/gradients/main.cpp +++ b/playground/gradients/main.cpp @@ -45,12 +45,20 @@ namespace } QskGradient gradient; -#if 1 - gradient.setLinearDirection( 0.0, 0.0, 1.0, 1.0 ); +#if 0 + gradient.setLinearDirection( 0.0, 1.0, 1.0, 0.0 ); #endif #if 0 + gradient.setLinearDirection( 1.0, 1.0, 0.0, 0.0 ); +#endif +#if 1 gradient.setLinearDirection( 0.2, 0.2, 0.7, 0.5 ); - gradient.setSpreadMode( QskGradient::PadSpread ); +#endif +#if 0 + gradient.setLinearDirection( -0.2, -0.1, 1.2, 1.3 ); +#endif +#if 0 + gradient.setLinearDirection( 0.5, -0.5, 0.5, 1.5 ); #endif #if 0 gradient.setRadialDirection( 0.25, 0.75, 0.25, 0.0 ); @@ -88,7 +96,7 @@ int main( int argc, char** argv ) QskWindow window; window.addItem( new MainView() ); - window.resize( 600, 600 ); + window.resize( 800, 600 ); window.show(); return app.exec(); diff --git a/src/nodes/QskBoxRectangleNode.cpp b/src/nodes/QskBoxRectangleNode.cpp index 383e0bc9..0c269ea7 100644 --- a/src/nodes/QskBoxRectangleNode.cpp +++ b/src/nodes/QskBoxRectangleNode.cpp @@ -44,19 +44,7 @@ static inline QskGradient qskEffectiveGradient( const QskGradient& gradient ) { auto g = gradient.effectiveGradient(); - if ( g.type() == QskGradient::Linear ) - { - auto dir = g.linearDirection(); - - if ( dir.isTilted() ) - { - dir.setStart( 0.0, 0.0 ); - dir.setStop( 1.0, 1.0 ); - - g.setLinearDirection( dir ); - } - } - else + if ( g.type() != QskGradient::Linear ) { qWarning() << "QskBoxRectangleNode does not support radial/conic gradients"; g.setDirection( QskGradient::Linear ); diff --git a/src/nodes/QskBoxRenderer.cpp b/src/nodes/QskBoxRenderer.cpp index fdbafee3..355d4f44 100644 --- a/src/nodes/QskBoxRenderer.cpp +++ b/src/nodes/QskBoxRenderer.cpp @@ -4,6 +4,8 @@ *****************************************************************************/ #include "QskBoxRenderer.h" +#include "QskRectRenderer.h" +#include "QskRoundedRectRenderer.h" #include "QskBoxShapeMetrics.h" #include "QskGradient.h" @@ -14,9 +16,9 @@ void QskBoxRenderer::renderBorder( const QskBoxBorderMetrics& border, QSGGeometry& geometry ) { if ( shape.isRectangle() ) - renderRectBorder( rect, border, geometry ); + QskRectRenderer::renderBorder( rect, border, geometry ); else - renderRectellipseBorder( rect, shape, border, geometry ); + QskRoundedRectRenderer::renderRectellipseBorder( rect, shape, border, geometry ); } void QskBoxRenderer::renderFill( @@ -24,9 +26,9 @@ void QskBoxRenderer::renderFill( const QskBoxBorderMetrics& border, QSGGeometry& geometry ) { if ( shape.isRectangle() ) - renderRectFill( rect, border, geometry ); + QskRectRenderer::renderFill( rect, border, geometry ); else - renderRectellipseFill( rect, shape, border, geometry ); + QskRoundedRectRenderer::renderRectellipseFill( rect, shape, border, geometry ); } void QskBoxRenderer::renderBox( const QRectF& rect, @@ -35,39 +37,68 @@ void QskBoxRenderer::renderBox( const QRectF& rect, QSGGeometry& geometry ) { if ( shape.isRectangle() ) - renderRect( rect, border, borderColors, gradient, geometry ); + { + QskRectRenderer::renderRect( + rect, border, borderColors, gradient, geometry ); + } else - renderRectellipse( rect, shape, border, borderColors, gradient, geometry ); + { + QskRoundedRectRenderer::renderRectellipse( + rect, shape, border, borderColors, gradient, geometry ); + } } bool QskBoxRenderer::isGradientSupported( - const QskBoxShapeMetrics&, const QskGradient& gradient ) + const QskBoxShapeMetrics& shape, const QskGradient& gradient ) { - if ( !gradient.isVisible() || gradient.isMonochrome() - || ( gradient.type() == QskGradient::Stops ) ) - { + if ( !gradient.isVisible() || gradient.isMonochrome() ) return true; + + if ( gradient.spreadMode() != QskGradient::PadSpread ) + { + // Only true if the situation requires spreading TODO ... + return false; } - if ( gradient.type() == QskGradient::Linear ) + switch( gradient.type() ) { - const auto dir = gradient.linearDirection(); - - if ( dir.isTilted() ) + case QskGradient::Stops: { - if ( dir.x1() == 0.0 && dir.y1() == 0.0 - && dir.x2() == 1.0 && dir.y2() == 1.0 ) + // will be rendered as vertical linear gradient + return true; + } + case QskGradient::Linear: + { + if ( shape.isRectangle() ) { + // rectangles are fully supported return true; } - } - else - { - if ( dir.x1() == 0.0 && dir.x2() == 1.0 ) - return true; + else + { + /* + For rounded rectangles we currently support + only the most common use cases. TODO ... + */ - if ( dir.y1() == 0.0 && dir.y2() == 1.0 ) - return true; + const auto dir = gradient.linearDirection(); + if ( dir.isTilted() ) + { + return ( dir.x1() == 0.0 && dir.x2() == 0.0 ) + && ( dir.y1() == 1.0 && dir.y2() == 1.0 ); + } + else + { + return ( dir.x1() == 0.0 && dir.x2() == 1.0 ) + || ( dir.y1() == 0.0 && dir.y2() == 1.0 ); + } + } + } + + default: + { + // Radial/Conical gradients have to be done with QskGradientMaterial + return false; } } diff --git a/src/nodes/QskBoxRenderer.h b/src/nodes/QskBoxRenderer.h index 2e56927b..d626aadd 100644 --- a/src/nodes/QskBoxRenderer.h +++ b/src/nodes/QskBoxRenderer.h @@ -59,15 +59,6 @@ class QSK_EXPORT QskBoxRenderer }; private: - static void renderRectFill( const QRectF&, - const QskBoxBorderMetrics&, QSGGeometry& ); - - static void renderRectBorder( const QRectF&, - const QskBoxBorderMetrics&, QSGGeometry& ); - - static void renderRect( const QRectF&, const QskBoxBorderMetrics&, - const QskBoxBorderColors&, const QskGradient&, QSGGeometry& ); - static void renderRectellipseFill( const QRectF&, const QskBoxShapeMetrics&, const QskBoxBorderMetrics&, QSGGeometry& ); @@ -80,9 +71,6 @@ class QSK_EXPORT QskBoxRenderer static void renderDiagonalFill( const Metrics&, const QskGradient&, int lineCount, QskVertex::ColoredLine* ); - - static void renderRectFill( const QskVertex::Quad&, - const QskGradient&, QskVertex::ColoredLine* ); }; #endif diff --git a/src/nodes/QskBoxRendererColorMap.h b/src/nodes/QskBoxRendererColorMap.h index af1e90ee..9b923e50 100644 --- a/src/nodes/QskBoxRendererColorMap.h +++ b/src/nodes/QskBoxRendererColorMap.h @@ -88,72 +88,11 @@ namespace QskVertex } }; - class SolidColorIterator : public ColorIterator - { - public: - inline SolidColorIterator( const QColor& color ) - : m_color( color ) - { - } - - inline Color colorAt( qreal ) const - { - return m_color; - } - - private: - const Color m_color; - }; - - class TwoColorIterator01 : public ColorIterator - { - public: - inline TwoColorIterator01( const QColor& color1, const QColor& color2 ) - : m_color1( color1 ) - , m_color2( color2 ) - { - } - - inline Color colorAt( qreal value ) const - { - return m_color1.interpolatedTo( m_color2, value ); - } - - private: - const Color m_color1, m_color2; - }; - - class TwoColorIterator : public ColorIterator - { - public: - inline TwoColorIterator( qreal value1, qreal value2, - const QColor& color1, const QColor& color2 ) - : m_value1( value1 ) - , m_range( value2 - value1 ) - , m_color1( color1 ) - , m_color2( color2 ) - { - } - - inline Color colorAt( qreal value ) const - { - const qreal r = ( value - m_value1 ) / m_range; - return m_color1.interpolatedTo( m_color2, r ); - } - - private: - const qreal m_value1, m_range; - const Color m_color1, m_color2; - }; - class GradientColorIterator : public ColorIterator { public: - inline GradientColorIterator( qreal value1, qreal value2, - const QskGradientStops& stops ) - : m_value1( value1 ) - , m_value2( value2 ) - , m_stops( stops ) + inline GradientColorIterator( const QskGradientStops& stops ) + : m_stops( stops ) { if ( stops.first().position() > 0.0 ) { @@ -171,8 +110,8 @@ namespace QskVertex if ( stops.last().position() < 1.0 ) m_finalIndex++; - m_valueStep1 = value1; - m_valueStep2 = valueAt( stops[ m_index ].position() ); + m_valueStep1 = 0.0; + m_valueStep2 = stops[ m_index ].position(); m_stepSize = m_valueStep2 - m_valueStep1; } @@ -202,14 +141,14 @@ namespace QskVertex if ( m_index >= m_stops.count() ) { m_color2 = m_color1; - m_valueStep2 = valueAt( 1.0 ); + m_valueStep2 = 1.0; } else { const auto& stop = m_stops[ m_index ]; m_color2 = stop.rgb(); - m_valueStep2 = valueAt( stop.position() ); + m_valueStep2 = stop.position(); } m_stepSize = m_valueStep2 - m_valueStep1; @@ -223,12 +162,6 @@ namespace QskVertex } private: - inline qreal valueAt( qreal pos ) const - { - return m_value1 + pos * ( ( m_value2 - m_value1 ) ); - } - - const qreal m_value1, m_value2; const QskGradientStops m_stops; int m_index, m_finalIndex; @@ -244,11 +177,14 @@ namespace QskVertex { while ( !colorIt.isDone() && ( colorIt.value() < contourIt.value() ) ) { - contourIt.setGradientLine( colorIt, line++ ); + if ( contourIt.setGradientLine( colorIt.value(), colorIt.color(), line ) ) + line++; + colorIt.advance(); } - contourIt.setContourLine( colorIt, line++ ); + const auto color = colorIt.colorAt( contourIt.value() ); + contourIt.setContourLine( color, line++ ); } while ( contourIt.advance() ); @@ -257,26 +193,16 @@ namespace QskVertex template< class ContourIterator > ColoredLine* fillOrdered( ContourIterator& contourIt, - qreal value1, qreal value2, const QskGradient& gradient, ColoredLine* line ) + const QskGradient& gradient, ColoredLine* line ) { if ( gradient.stepCount() == 1 ) { - if ( value2 == 1.0 && value1 == 0.0 ) - { - TwoColorIterator01 colorIt( gradient.rgbStart(), gradient.rgbEnd() ); - line = fillOrdered( contourIt, colorIt, line ); - } - else - { - TwoColorIterator colorIt( value1, value2, - gradient.rgbStart(), gradient.rgbEnd() ); - - line = fillOrdered( contourIt, colorIt, line ); - } + GradientColorIterator colorIt( gradient.stops() ); + line = fillOrdered( contourIt, colorIt, line ); } else { - GradientColorIterator colorIt( value1, value2, gradient.stops() ); + GradientColorIterator colorIt( gradient.stops() ); line = fillOrdered( contourIt, colorIt, line ); } diff --git a/src/nodes/QskBoxRendererDEllipse.cpp b/src/nodes/QskBoxRendererDEllipse.cpp index ade0f637..92834776 100644 --- a/src/nodes/QskBoxRendererDEllipse.cpp +++ b/src/nodes/QskBoxRendererDEllipse.cpp @@ -3,7 +3,7 @@ * This file may be used under the terms of the QSkinny License, Version 1.0 *****************************************************************************/ -#include "QskBoxRenderer.h" +#include "QskRoundedRectRenderer.h" #include "QskBoxRendererColorMap.h" #include "QskGradient.h" #include "QskVertex.h" @@ -44,7 +44,7 @@ namespace class ValueCurve { public: - ValueCurve( const QskBoxRenderer::Metrics& m ) + ValueCurve( const QskRoundedRectRenderer::Metrics& m ) { /* The slopes of the value line and those for the fill lines. @@ -127,7 +127,7 @@ namespace { } - void setup( const QskBoxRenderer::Metrics& metrics, + void setup( const QskRoundedRectRenderer::Metrics& metrics, bool isLeading, bool clockwise, qreal cos, qreal cosStep, qreal sin, qreal sinStep, qreal x1, qreal y1, qreal v1, qreal x2, qreal y2, qreal v2 ) @@ -167,7 +167,9 @@ namespace inline const ContourLine& contourLine() const { return m_contourLine; } - inline void advance( const QskBoxRenderer::Metrics& metrics, const ValueCurve& curve ) + inline void advance( + const QskRoundedRectRenderer::Metrics& metrics, + const ValueCurve& curve ) { if ( m_isDone ) return; @@ -318,7 +320,7 @@ namespace static constexpr qreal m_eps = 1e-4; inline void setCorner( - Qt::Corner corner, const QskBoxRenderer::Metrics& metrics ) + Qt::Corner corner, const QskRoundedRectRenderer::Metrics& metrics ) { m_corner = corner; const auto& c = metrics.corner[ corner ]; @@ -412,7 +414,7 @@ namespace class OutlineIterator { public: - OutlineIterator( const QskBoxRenderer::Metrics& metrics, + OutlineIterator( const QskRoundedRectRenderer::Metrics& metrics, const ValueCurve& curve, bool clockwise ) : m_metrics( metrics ) , m_curve( curve ) @@ -549,7 +551,7 @@ namespace line->setLine( x1, y1, x2, y2, color ); } - const QskBoxRenderer::Metrics& m_metrics; + const QskRoundedRectRenderer::Metrics& m_metrics; const ValueCurve& m_curve; /* @@ -564,23 +566,23 @@ namespace { public: DRectellipseIterator( - const QskBoxRenderer::Metrics& metrics, const ValueCurve& curve ) + const QskRoundedRectRenderer::Metrics& metrics, + const ValueCurve& curve ) : m_left( metrics, curve, false ) , m_right( metrics, curve, true ) { m_next = ( m_left.value() < m_right.value() ) ? &m_left : &m_right; } - template< class ColorIterator > - inline void setGradientLine( const ColorIterator& it, ColoredLine* line ) + inline bool setGradientLine( qreal value, Color color, ColoredLine* line ) { - m_next->setLineAt( it.value(), it.color(), line ); + m_next->setLineAt( value, color, line ); + return true; } - template< class ColorIterator > - inline void setContourLine( const ColorIterator& it, ColoredLine* line ) + inline void setContourLine( Color color, ColoredLine* line ) { - m_next->setLine( it.colorAt( m_next->value() ), line ); + m_next->setLine( color, line ); } inline qreal value() const @@ -602,13 +604,13 @@ namespace }; } -void QskBoxRenderer::renderDiagonalFill( const QskBoxRenderer::Metrics& metrics, +void QskRoundedRectRenderer::renderDiagonalFill( const QskRoundedRectRenderer::Metrics& metrics, const QskGradient& gradient, int fillLineCount, QskVertex::ColoredLine* lines ) { const ValueCurve curve( metrics ); DRectellipseIterator it( metrics, curve ); - auto line = QskVertex::fillOrdered( it, 0.0, 1.0, gradient, lines ); + auto line = QskVertex::fillOrdered( it, gradient, lines ); /* There are a couple of reasons, why less points have been rendered diff --git a/src/nodes/QskBoxRendererRect.cpp b/src/nodes/QskBoxRendererRect.cpp deleted file mode 100644 index 76dc83d1..00000000 --- a/src/nodes/QskBoxRendererRect.cpp +++ /dev/null @@ -1,654 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) 2016 Uwe Rathmann - * This file may be used under the terms of the QSkinny License, Version 1.0 - *****************************************************************************/ - -#include "QskBoxBorderColors.h" -#include "QskBoxBorderMetrics.h" -#include "QskBoxRenderer.h" -#include "QskBoxRendererColorMap.h" -#include "QskFunctions.h" -#include "QskGradientDirection.h" -#include "QskVertex.h" - -using namespace QskVertex; - -namespace -{ - class VRectIterator - { - public: - inline VRectIterator( const Quad& rect ) - : m_rect( rect ) - , m_value( rect.top ) - { - } - - template< class ColorIterator > - inline void setGradientLine( const ColorIterator& it, ColoredLine* line ) - { - line->setHLine( m_rect.left, m_rect.right, it.value(), it.color() ); - } - - template< class ColorIterator > - inline void setContourLine( const ColorIterator& it, ColoredLine* line ) - { - line->setHLine( m_rect.left, m_rect.right, m_value, it.colorAt( m_value ) ); - } - - inline qreal value() const - { - return m_value; - } - - inline bool advance() - { - if ( m_value == m_rect.top ) - { - m_value = m_rect.bottom; - return true; - } - - return false; - } - - private: - const Quad& m_rect; - qreal m_value; - }; - - class HRectIterator - { - public: - inline HRectIterator( const Quad& rect ) - : m_rect( rect ) - , m_value( rect.left ) - { - } - - template< class ColorIterator > - inline void setGradientLine( const ColorIterator& it, ColoredLine* line ) - { - line->setVLine( it.value(), m_rect.top, m_rect.bottom, it.color() ); - } - - template< class ColorIterator > - inline void setContourLine( const ColorIterator& it, ColoredLine* line ) - { - line->setVLine( m_value, m_rect.top, m_rect.bottom, it.colorAt( m_value ) ); - } - - inline qreal value() const - { - return m_value; - } - - inline bool advance() - { - if ( m_value == m_rect.left ) - { - m_value = m_rect.right; - return true; - } - - return false; - } - - private: - const Quad& m_rect; - qreal m_value; - }; - - class DSquareIterator - { - public: - inline DSquareIterator( const Quad& rect ) - : m_rect( rect ) - , m_step( 0 ) - { - Q_ASSERT( rect.width == rect.height ); - } - - template< class ColorIterator > - inline void setGradientLine( const ColorIterator& it, ColoredLine* line ) - { - const auto v = it.value(); - - if ( v <= 0.5 ) - { - const qreal dt = m_rect.width * 2 * v; - - line->setLine( m_rect.left, m_rect.top + dt, - m_rect.left + dt, m_rect.top, it.color() ); - } - else - { - const qreal dt = m_rect.width * 2 * ( v - 0.5 ); - line->setLine( m_rect.left + dt, m_rect.bottom, - m_rect.right, m_rect.top + dt, it.color() ); - } - } - - template< class ColorIterator > - inline void setContourLine( const ColorIterator& it, ColoredLine* line ) - { - const auto color = it.colorAt( value() ); - const auto& r = m_rect; - - switch ( m_step ) - { - case 0: - { - line->setLine( r.left, r.top, r.left, r.top, color ); - break; - } - case 1: - { - line->setLine( r.left, r.bottom, r.right, r.top, color ); - break; - } - case 2: - { - line->setLine( r.right, r.bottom, r.right, r.bottom, color ); - break; - } - } - } - - inline qreal value() const - { - return m_step * 0.5; - } - - inline bool advance() - { - return ++m_step <= 2; - } - - private: - const Quad& m_rect; - int m_step; - }; - - class DRectIterator - { - public: - inline DRectIterator( const Quad& rect ) - : m_rect( rect ) - , m_step( 0 ) - { - const qreal w = rect.width; - const qreal h = rect.height; - - Q_ASSERT( w != h ); - - const qreal w2 = w * w; - const qreal h2 = h * h; - - m_fx = ( w2 + h2 ) / w; - m_fy = ( w2 + h2 ) / h; - - m_lx = m_rect.top - h2 / w; - m_ly = m_rect.left - w2 / h; - - m_valueTR = w2 / ( w2 + h2 ); - m_valueBL = h2 / ( w2 + h2 ); - } - - template< class ColorIterator > - inline void setGradientLine( const ColorIterator& it, ColoredLine* line ) - { - const auto v = it.value(); - const auto color = it.color(); - const auto& r = m_rect; - - switch ( m_step ) - { - case 1: - { - const qreal dx = v * m_fx; - const qreal dy = v * m_fy; - - line->setLine( r.left, r.top + dy, r.left + dx, r.top, color ); - break; - } - case 2: - { - if ( r.width > r.height ) - { - const qreal dx = v * m_fx; - line->setLine( m_lx + dx, r.bottom, r.left + dx, r.top, color ); - } - else - { - const qreal dy = v * m_fy; - line->setLine( r.left, r.top + dy, r.right, m_ly + dy, color ); - } - break; - } - case 3: - { - const qreal dx = v * m_fx; - const qreal dy = v * m_fy; - - line->setLine( m_lx + dx, r.bottom, r.right, m_ly + dy, color ); - break; - } - } - } - - template< class ColorIterator > - inline void setContourLine( const ColorIterator& it, ColoredLine* line ) - { - const auto& r = m_rect; - - switch ( m_step ) - { - case 0: - { - line->setLine( r.left, r.top, - r.left, r.top, it.colorAt( 0.0 ) ); - - break; - } - case 1: - { - if ( r.width >= r.height ) - { - const qreal dx = m_valueBL * m_fx; - - line->setLine( r.left, r.bottom, - r.left + dx, r.top, it.colorAt( m_valueBL ) ); - } - else - { - const qreal dy = m_valueTR * m_fy; - - line->setLine( r.left, r.top + dy, - r.right, r.top, it.colorAt( m_valueTR ) ); - } - - break; - } - case 2: - { - if ( r.width >= r.height ) - { - const qreal dx = m_valueTR * m_fx; - - line->setLine( r.left + dx, r.bottom, - r.right, r.top, it.colorAt( m_valueTR ) ); - } - else - { - const qreal dy = m_valueBL * m_fy; - - line->setLine( r.left, r.bottom, - r.right, r.top + dy, it.colorAt( m_valueBL ) ); - } - - break; - } - case 3: - { - line->setLine( r.right, r.bottom, - r.right, r.bottom, it.colorAt( 1.0 ) ); - - break; - } - default: - { - Q_ASSERT( false ); - } - } - } - - inline qreal value() const - { - switch ( m_step ) - { - case 0: - return 0.0; - - case 1: - return std::min( m_valueBL, m_valueTR ); - - case 2: - return std::max( m_valueBL, m_valueTR ); - - default: - return 1.0; - } - } - - inline bool advance() - { - return ++m_step <= 3; - } - - private: - const Quad& m_rect; - - qreal m_fx, m_fy; - qreal m_lx, m_ly; - qreal m_valueTR, m_valueBL; - - int m_step; - }; -} - -static inline void qskCreateFillOrdered( const Quad& rect, - const QskGradient& gradient, ColoredLine* line ) -{ - const auto dir = gradient.linearDirection(); - - if ( dir.isHorizontal() ) - { - HRectIterator it( rect ); - line = QskVertex::fillOrdered( it, rect.left, rect.right, gradient, line ); - } - else if ( dir.isVertical() ) - { - VRectIterator it( rect ); - line = QskVertex::fillOrdered( it, rect.top, rect.bottom, gradient, line ); - } - else - { - if ( rect.width == rect.height ) - { - DSquareIterator it( rect ); - line = QskVertex::fillOrdered( it, 0.0, 1.0, gradient, line ); - } - else - { - DRectIterator it( rect ); - line = QskVertex::fillOrdered( it, 0.0, 1.0, gradient, line ); - } - } -} - -template< class ColorMap, class Line > -static inline void qskCreateFillRandom( Qt::Orientation orientation, - const Quad& r, const ColorMap& map, Line* line ) -{ - if ( orientation == Qt::Vertical ) - { - ( line++ )->setLine( r.left, r.top, r.right, r.top, map.colorAt( 0.0 ) ); - ( line++ )->setLine( r.left, r.bottom, r.right, r.bottom, map.colorAt( 1.0 ) ); - } - else - { - ( line++ )->setLine( r.left, r.top, r.left, r.bottom, map.colorAt( 0.0 ) ); - ( line++ )->setLine( r.right, r.top, r.right, r.bottom, map.colorAt( 1.0 ) ); - } -} - -template< class Line > -static inline void qskCreateBorderMonochrome( - const Quad& out, const Quad& in, Color color, Line* line ) -{ - auto l = line; - - ( l++ )->setLine( in.right, in.bottom, out.right, out.bottom, color ); - ( l++ )->setLine( in.left, in.bottom, out.left, out.bottom, color ); - ( l++ )->setLine( in.left, in.top, out.left, out.top, color ); - ( l++ )->setLine( in.right, in.top, out.right, out.top, color ); - - *l = line[ 0 ]; -} - -template< class Line > -static inline void qskCreateBorder( - const Quad& out, const Quad& in, - const QskBoxBorderColors& colors, Line* line ) -{ - const qreal dx1 = in.right - in.left; - const qreal dx2 = out.right - out.left; - const qreal dy1 = in.top - in.bottom; - const qreal dy2 = out.top - out.bottom; - - { - const auto stops = colors.bottom().stops(); - - if ( stops.first().position() > 0.0 ) - { - ( line++ )->setLine( in.right, in.bottom, - out.right, out.bottom, stops.first().rgb() ); - } - - for( const auto& stop : stops ) - { - const qreal x1 = in.right - stop.position() * dx1; - const qreal x2 = out.right - stop.position() * dx2; - - ( line++ )->setLine( x1, in.bottom, x2, out.bottom, stop.rgb() ); - } - - if ( stops.last().position() < 1.0 ) - { - ( line++ )->setLine( in.left, in.bottom, - out.left, out.bottom, stops.last().rgb() ); - } - } - - { - const auto stops = colors.left().stops(); - - if ( stops.first().position() > 0.0 ) - { - ( line++ )->setLine( in.left, in.bottom, - out.left, out.bottom, stops.first().rgb() ); - } - - for( const auto& stop : stops ) - { - const qreal y1 = in.bottom + stop.position() * dy1; - const qreal y2 = out.bottom + stop.position() * dy2; - - ( line++ )->setLine( in.left, y1, out.left, y2, stop.rgb() ); - } - - if ( stops.last().position() < 1.0 ) - { - ( line++ )->setLine( in.left, in.top, - out.left, out.top, stops.last().rgb() ); - } - } - - { - const auto stops = colors.top().stops(); - - if ( stops.first().position() > 0.0 ) - { - ( line++ )->setLine( in.left, in.top, - out.left, out.top, stops.first().rgb() ); - } - - for( const auto& stop : stops ) - { - const qreal x1 = in.left + stop.position() * dx1; - const qreal x2 = out.left + stop.position() * dx2; - - ( line++ )->setLine( x1, in.top, x2, out.top, stop.rgb() ); - } - - if ( stops.last().position() < 1.0 ) - { - ( line++ )->setLine( in.right, in.top, - out.right, out.top, stops.last().rgb() ); - } - } - - { - const auto stops = colors.right().stops(); - - if ( stops.first().position() > 0.0 ) - { - ( line++ )->setLine( in.right, in.top, - out.right, out.top, stops.first().rgb() ); - } - - for( const auto& stop : stops ) - { - const qreal y1 = in.bottom + ( 1 - stop.position() ) * dy1; - const qreal y2 = out.bottom + ( 1 - stop.position() ) * dy2; - - ( line++ )->setLine( in.right, y1, out.right, y2, stop.rgb() ); - } - - if ( stops.last().position() < 1.0 ) - { - ( line++ )->setLine( in.right, in.bottom, - out.right, out.bottom, stops.last().rgb() ); - } - } -} - -void QskBoxRenderer::renderRectBorder( const QRectF& rect, - const QskBoxBorderMetrics& border, QSGGeometry& geometry ) -{ - const Quad out = rect; - const Quad in = qskValidOrEmptyInnerRect( rect, border.widths() ); - - if ( out == in ) - { - allocateLines< Line >( geometry, 0 ); - return; - } - - const auto line = allocateLines< Line >( geometry, 4 + 1 ); - qskCreateBorderMonochrome( out, in, Color(), line ); -} - -void QskBoxRenderer::renderRectFill( const QRectF& rect, - const QskBoxBorderMetrics& border, QSGGeometry& geometry ) -{ - const Quad quad = qskValidOrEmptyInnerRect( rect, border.widths() ); - - if ( quad.isEmpty() ) - { - geometry.allocate( 0 ); - return; - } - - 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, - const QskBoxBorderMetrics& border, const QskBoxBorderColors& borderColors, - const QskGradient& gradient, QSGGeometry& geometry ) -{ - const Quad out = rect; - const Quad in = qskValidOrEmptyInnerRect( rect, border.widths() ); - - int fillLineCount = 0; - if ( gradient.isVisible() && !in.isEmpty() ) - { - fillLineCount = gradient.stepCount() + 1; - - if ( !gradient.isMonochrome() && gradient.linearDirection().isTilted() ) - { - // extra lines for the corners - - fillLineCount++; - if ( in.width != in.height ) - fillLineCount++; - } - } - - int borderLineCount = 0; - if ( in != out ) - { - const auto& bc = borderColors; - - if ( bc.isVisible() ) - { - // We can build a rectangular border from the 4 diagonal - // lines at the corners, but need an additional line - // for closing the border. - - borderLineCount = 4 + 1; - - if ( !bc.isMonochrome() ) - { - // we might need extra lines to separate colors - // at the non closing corners - - // ### As an optimization we could check orientation and colors - // to test whether colors are the same - const int additionalLines = -1 - + bc.left().stepCount() - + bc.top().stepCount() - + bc.right().stepCount() - + bc.bottom().stepCount(); - - borderLineCount += qMax( additionalLines, 0 ); - } - } - } - - auto line = allocateLines< ColoredLine >( geometry, borderLineCount + fillLineCount ); - - if ( fillLineCount > 0 ) - { - if ( gradient.isMonochrome() ) - { - const ColorMapSolid colorMap( gradient ); - qskCreateFillRandom( Qt::Vertical, in, colorMap, line ); - } - else - { - bool fillRandom = gradient.stepCount() <= 1; - if ( fillRandom ) - { - /* - Not necessarily a requirement for being ordered, - but we didn't implement a random fill algo for - diagonal gradients yet. - */ - fillRandom = !gradient.linearDirection().isTilted(); - } - - if ( fillRandom ) - { - const auto orientation = gradient.linearDirection().isVertical() - ? Qt::Vertical : Qt::Horizontal; - - const ColorMapGradient colorMap( gradient ); - qskCreateFillRandom( orientation, in, colorMap, line ); - } - else - { - qskCreateFillOrdered( in, gradient, line ); - } - } - } - - if ( borderLineCount > 0 ) - { - const auto& bc = borderColors; - auto fillLines = line + fillLineCount; - - if ( bc.isMonochrome() ) - { - const auto rgb = bc.left().rgbStart(); - qskCreateBorderMonochrome( rect, in, Color( rgb ), fillLines ); - } - else - { - qskCreateBorder( rect, in, bc, fillLines ); - } - } -} - -void QskBoxRenderer::renderRectFill( const QskVertex::Quad& rect, - const QskGradient& gradient, QskVertex::ColoredLine* line ) -{ - qskCreateFillOrdered( rect, gradient, line ); -} diff --git a/src/nodes/QskRectRenderer.cpp b/src/nodes/QskRectRenderer.cpp new file mode 100644 index 00000000..dc8bd76f --- /dev/null +++ b/src/nodes/QskRectRenderer.cpp @@ -0,0 +1,551 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#include "QskBoxBorderColors.h" +#include "QskBoxBorderMetrics.h" +#include "QskRectRenderer.h" +#include "QskBoxRendererColorMap.h" +#include "QskGradient.h" +#include "QskGradientDirection.h" +#include "QskFunctions.h" +#include "QskVertex.h" + +using namespace QskVertex; + +namespace +{ + class HVRectIterator + { + public: + inline HVRectIterator( const Quad& rect, const QLineF& vector ) + : m_rect( rect ) + , m_vertical( vector.x1() == vector.x2() ) + { + if ( m_vertical ) + { + m_t = rect.top + vector.y1() * rect.height; + m_dt = vector.dy() * rect.height; + + m_values[0] = ( rect.top - m_t ) / m_dt; + m_values[1] = ( rect.bottom - m_t ) / m_dt; + } + else + { + m_t = rect.left + vector.x1() * rect.width; + m_dt = vector.dx() * rect.width; + + m_values[0] = ( rect.left - m_t ) / m_dt; + m_values[1] = ( rect.right - m_t ) / m_dt; + } + } + + 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 ) + line->setHLine( m_rect.left, m_rect.right, v, color ); + else + line->setVLine( v, m_rect.top, m_rect.bottom, color ); + + return true; + } + + inline void setContourLine( Color color, ColoredLine* line ) + { + if ( m_vertical ) + { + const auto y = m_step ? m_rect.bottom : m_rect.top; + line->setLine( m_rect.left, y, m_rect.right, y, color ); + } + else + { + const auto x = m_step ? m_rect.right : m_rect.left; + line->setLine( x, m_rect.top, x, m_rect.bottom, color ); + } + } + + inline qreal value() const { return m_values[ m_step ? 1 : 0 ]; } + inline bool advance() { return m_step++ == 0; } + + private: + const Quad& m_rect; + qreal m_t, m_dt; + qreal m_values[2]; + const bool m_vertical; + int m_step = 0; + }; + + class DRectIterator + { + public: + inline DRectIterator( const Quad& quad, const QLineF& vector ) + { + m_v.x = quad.left + vector.x1() * quad.width; + m_v.y = quad.top + vector.y1() * quad.height; + m_v.dx = vector.dx() * quad.width; + m_v.dy = vector.dy() * quad.height; + + /* + We calculate the values at the corners and order them + in increasing order + */ + + const qreal lx = ( quad.left - m_v.x ) * m_v.dx; + const qreal rx = ( quad.right - m_v.x ) * m_v.dx; + const qreal ty = ( quad.top - m_v.y ) * m_v.dy; + const qreal by = ( quad.bottom - m_v.y ) * m_v.dy; + + const qreal dot = m_v.dx * m_v.dx + m_v.dy * m_v.dy; + + const qreal tl = ( lx + ty ) / dot; + const qreal tr = ( rx + ty ) / dot; + const qreal bl = ( lx + by ) / dot; + const qreal br = ( rx + by ) / dot; + + if ( ( m_v.dy >= 0.0 ) == ( m_v.dx >= 0.0 ) ) + { + m_corners[0] = { { quad.left, quad.top }, tl }; + m_corners[1] = { { quad.right, quad.top }, tr }; + m_corners[2] = { { quad.left, quad.bottom }, bl }; + m_corners[3] = { { quad.right, quad.bottom }, br }; + } + else + { + m_corners[0] = { { quad.left, quad.bottom }, bl }; + m_corners[1] = { { quad.right, quad.bottom }, br }; + m_corners[2] = { { quad.left, quad.top }, tl }; + m_corners[3] = { { quad.right, quad.top }, tr }; + } + + if ( m_corners[0].value > m_corners[3].value ) + qSwap( m_corners[0], m_corners[3] ); + + if ( m_corners[1].value > m_corners[2].value ) + qSwap( m_corners[1], m_corners[2] ); + } + + 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; + const qreal y = m_v.y + m_v.dy * value; + + const bool on = m_corners[0].pos.x() == m_corners[1].pos.x(); + + QPointF p1, p2; + + switch( m_step ) + { + case 1: + { + p1 = p2 = m_corners[0].pos; + + if ( on ) + { + p1.ry() = y + ( x - p1.x() ) / m; + p2.rx() = x + ( y - p2.y() ) * m; + } + else + { + p1.rx() = x + ( y - p1.y() ) * m; + p2.ry() = y + ( x - p2.x() ) / m; + } + + break; + } + case 2: + { + p1 = m_corners[1].pos; + p2 = m_corners[0].pos; + + if ( on ) + { + p1.rx() = x + ( y - p1.y() ) * m; + p2.rx() = x + ( y - p2.y() ) * m; + } + else + { + p1.ry() = y + ( x - p1.x() ) / m; + p2.ry() = y + ( x - p2.x() ) / m; + } + + break; + } + case 3: + { + p1 = m_corners[1].pos; + p2 = m_corners[2].pos; + + if ( on ) + { + p1.rx() = x + ( y - p1.y() ) * m; + p2.ry() = y + ( x - p2.x() ) / m; + } + else + { + p1.ry() = y + ( x - p1.x() ) / m; + p2.rx() = x + ( y - p2.y() ) * m; + } + break; + } + } + + if ( p1.x() < p2.x() ) + line->setLine( p1.x(), p1.y(), p2.x(), p2.y(), color ); + else + line->setLine( p2.x(), p2.y(), p1.x(), p1.y(), color ); + + return true; + } + + inline void setContourLine( Color color, ColoredLine* line ) + { + if( m_step == 0 || m_step == 3 ) + { + 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; + + 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; + + if ( p1.x() <= p2.x() ) + line->setLine( p1.x(), p1.y(), p2.x(), p2.y(), color ); + else + line->setLine( p2.x(), p2.y(), p1.x(), p1.y(), color ); + } + } + + inline qreal value() const + { + return m_corners[ m_step ].value; + } + + inline bool advance() + { + return ++m_step <= 3; + } + + private: + struct { qreal x, y, dx, dy; } m_v; + struct { QPointF pos; qreal value; } m_corners[4]; + + int m_step = 0; + }; +} + +static ColoredLine* qskAddFillLines( const Quad& rect, + const QskGradient& gradient, ColoredLine* line ) +{ + const auto dir = gradient.linearDirection(); + + if ( dir.isTilted() ) + { + DRectIterator it( rect, dir.vector() ); + line = QskVertex::fillOrdered( it, gradient, line ); + } + else + { + HVRectIterator it( rect, dir.vector() ); + line = QskVertex::fillOrdered( it, gradient, line ); + } + + return line; +} + +template< class Line > +static inline Line* qskAddBorderLines( + const Quad& out, const Quad& in, Color color, Line* line ) +{ + line[0].setLine( in.right, in.bottom, out.right, out.bottom, color ); + line[1].setLine( in.left, in.bottom, out.left, out.bottom, color ); + line[2].setLine( in.left, in.top, out.left, out.top, color ); + line[3].setLine( in.right, in.top, out.right, out.top, color ); + line[4] = line[ 0 ]; + + return line + 5; +} + +template< class Line > +static inline Line* qskAddBorderLines( + const Quad& out, const Quad& in, + const QskBoxBorderColors& colors, Line* line ) +{ + const qreal dx1 = in.right - in.left; + const qreal dx2 = out.right - out.left; + const qreal dy1 = in.top - in.bottom; + const qreal dy2 = out.top - out.bottom; + + { + const auto stops = colors.bottom().stops(); + + if ( stops.first().position() > 0.0 ) + { + ( line++ )->setLine( in.right, in.bottom, + out.right, out.bottom, stops.first().rgb() ); + } + + for( const auto& stop : stops ) + { + const qreal x1 = in.right - stop.position() * dx1; + const qreal x2 = out.right - stop.position() * dx2; + + ( line++ )->setLine( x1, in.bottom, x2, out.bottom, stop.rgb() ); + } + + if ( stops.last().position() < 1.0 ) + { + ( line++ )->setLine( in.left, in.bottom, + out.left, out.bottom, stops.last().rgb() ); + } + } + + { + const auto stops = colors.left().stops(); + + if ( stops.first().position() > 0.0 ) + { + ( line++ )->setLine( in.left, in.bottom, + out.left, out.bottom, stops.first().rgb() ); + } + + for( const auto& stop : stops ) + { + const qreal y1 = in.bottom + stop.position() * dy1; + const qreal y2 = out.bottom + stop.position() * dy2; + + ( line++ )->setLine( in.left, y1, out.left, y2, stop.rgb() ); + } + + if ( stops.last().position() < 1.0 ) + { + ( line++ )->setLine( in.left, in.top, + out.left, out.top, stops.last().rgb() ); + } + } + + { + const auto stops = colors.top().stops(); + + if ( stops.first().position() > 0.0 ) + { + ( line++ )->setLine( in.left, in.top, + out.left, out.top, stops.first().rgb() ); + } + + for( const auto& stop : stops ) + { + const qreal x1 = in.left + stop.position() * dx1; + const qreal x2 = out.left + stop.position() * dx2; + + ( line++ )->setLine( x1, in.top, x2, out.top, stop.rgb() ); + } + + if ( stops.last().position() < 1.0 ) + { + ( line++ )->setLine( in.right, in.top, + out.right, out.top, stops.last().rgb() ); + } + } + + { + const auto stops = colors.right().stops(); + + if ( stops.first().position() > 0.0 ) + { + ( line++ )->setLine( in.right, in.top, + out.right, out.top, stops.first().rgb() ); + } + + for( const auto& stop : stops ) + { + const qreal y1 = in.bottom + ( 1 - stop.position() ) * dy1; + const qreal y2 = out.bottom + ( 1 - stop.position() ) * dy2; + + ( line++ )->setLine( in.right, y1, out.right, y2, stop.rgb() ); + } + + if ( stops.last().position() < 1.0 ) + { + ( line++ )->setLine( in.right, in.bottom, + out.right, out.bottom, stops.last().rgb() ); + } + } + + return line; +} + +void QskRectRenderer::renderBorder( const QRectF& rect, + const QskBoxBorderMetrics& border, QSGGeometry& geometry ) +{ + const Quad out = rect; + const Quad in = qskValidOrEmptyInnerRect( rect, border.widths() ); + + if ( out == in ) + { + allocateLines< Line >( geometry, 0 ); + return; + } + + const auto line = allocateLines< Line >( geometry, 4 + 1 ); + qskAddBorderLines( out, in, Color(), line ); +} + +void QskRectRenderer::renderFill0( const QskVertex::Quad& rect, + const QskGradient& gradient, QskVertex::ColoredLine* line ) +{ + qskAddFillLines( rect, gradient, line ); +} + +void QskRectRenderer::renderFill( const QRectF& rect, + const QskBoxBorderMetrics& border, QSGGeometry& geometry ) +{ + const Quad quad = qskValidOrEmptyInnerRect( rect, border.widths() ); + + if ( quad.isEmpty() ) + { + geometry.allocate( 0 ); + return; + } + + 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 QskRectRenderer::renderRect( const QRectF& rect, + const QskBoxBorderMetrics& border, const QskBoxBorderColors& borderColors, + const QskGradient& gradient, QSGGeometry& geometry ) +{ + const Quad out = rect; + const Quad in = qskValidOrEmptyInnerRect( rect, border.widths() ); + + int fillLineCount = 0; + if ( gradient.isVisible() && !in.isEmpty() ) + { + fillLineCount = gradient.stepCount() + 1; + + if ( !gradient.isMonochrome() && gradient.linearDirection().isTilted() ) + fillLineCount += 2; // contour lines for the corners + } + + int borderLineCount = 0; + if ( in != out && borderColors.isVisible() ) + { + // We can build a rectangular border from the 4 diagonal + // lines at the corners, but need an additional line + // for closing the border. + + borderLineCount = 4 + 1; + + if ( !borderColors.isMonochrome() ) + { + // we might need extra lines to separate colors + // at the non closing corners + + // ### As an optimization we could check orientation and colors + // to test whether colors are the same + const int additionalLines = -1 + + borderColors.left().stepCount() + + borderColors.top().stepCount() + + borderColors.right().stepCount() + + borderColors.bottom().stepCount(); + + borderLineCount += qMax( additionalLines, 0 ); + } + } + + const auto line = allocateLines< ColoredLine >( + geometry, borderLineCount + fillLineCount ); + + auto l = line; + + if ( fillLineCount > 0 ) + { + if ( gradient.isMonochrome() ) + { + const auto c = gradient.rgbStart(); + + ( l++ )->setHLine( rect.left(), rect.right(), rect.top(), c ); + ( l++ )->setHLine( rect.left(), rect.right(), rect.bottom(), c ); + } + else + { + if ( gradient.stepCount() <= 1 && !gradient.linearDirection().isTilted() ) + { + const auto c1 = gradient.rgbStart(); + const auto c2 = gradient.rgbEnd(); + + if ( gradient.linearDirection().isVertical() ) + { + ( l++ )->setHLine( rect.left(), rect.right(), rect.top(), c1 ); + ( l++ )->setHLine( rect.left(), rect.right(), rect.bottom(), c2 ); + } + else + { + ( l++ )->setVLine( rect.left(), rect.top(), rect.bottom(), c1 ); + ( l++ )->setVLine( rect.right(), rect.top(), rect.bottom(), c2 ); + } + } + else + { + l = qskAddFillLines( in, gradient, 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 ); + } + + if ( borderLineCount > 0 ) + { + if ( borderColors.isMonochrome() ) + { + const auto rgb = borderColors.left().rgbStart(); + l = qskAddBorderLines( rect, in, Color( rgb ), l ); + } + else + { + l = qskAddBorderLines( rect, in, borderColors, l ); + } + + Q_ASSERT( l - line == borderLineCount + fillLineCount ); + } +} diff --git a/src/nodes/QskRectRenderer.h b/src/nodes/QskRectRenderer.h new file mode 100644 index 00000000..c2925866 --- /dev/null +++ b/src/nodes/QskRectRenderer.h @@ -0,0 +1,47 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#ifndef QSK_RECT_RENDERER_H +#define QSK_RECT_RENDERER_H + +#include "QskGlobal.h" + +class QskBoxBorderMetrics; +class QskBoxBorderColors; +class QskGradient; + +class QSGGeometry; +class QRectF; + +namespace QskRectRenderer +{ + void renderBorder( const QRectF&, + const QskBoxBorderMetrics&, QSGGeometry& ); + + void renderFill( const QRectF&, + const QskBoxBorderMetrics&, QSGGeometry& ); + + void renderRect( const QRectF&, const QskBoxBorderMetrics&, + const QskBoxBorderColors&, const QskGradient&, QSGGeometry& ); +} + +#if 1 + +// maybe to QskVertex.h ??? +namespace QskVertex +{ + class Quad; + class ColoredLine; +} + +namespace QskRectRenderer +{ + void renderFill0( const QskVertex::Quad&, + const QskGradient&, QskVertex::ColoredLine* ); +} + +#endif + +#endif diff --git a/src/nodes/QskBoxRendererEllipse.cpp b/src/nodes/QskRoundedRectRenderer.cpp similarity index 93% rename from src/nodes/QskBoxRendererEllipse.cpp rename to src/nodes/QskRoundedRectRenderer.cpp index 3e7b0471..07c3b279 100644 --- a/src/nodes/QskBoxRendererEllipse.cpp +++ b/src/nodes/QskRoundedRectRenderer.cpp @@ -3,13 +3,14 @@ * This file may be used under the terms of the QSkinny License, Version 1.0 *****************************************************************************/ -#include "QskBoxRenderer.h" +#include "QskRoundedRectRenderer.h" #include "QskBoxBorderColors.h" #include "QskBoxBorderMetrics.h" #include "QskBoxRendererColorMap.h" #include "QskBoxShapeMetrics.h" #include "QskGradientDirection.h" +#include "QskRectRenderer.h" #include #include @@ -109,7 +110,7 @@ namespace class BorderValuesUniform { public: - inline BorderValuesUniform( const QskBoxRenderer::Metrics& metrics ) + inline BorderValuesUniform( const QskRoundedRectRenderer::Metrics& metrics ) : m_corner( metrics.corner[ 0 ] ) , m_dx1( m_corner.radiusInnerX ) , m_dy1( m_corner.radiusInnerY ) @@ -134,14 +135,14 @@ namespace inline qreal dy2( int ) const { return m_dy2; } private: - const QskBoxRenderer::Metrics::Corner& m_corner; + const QskRoundedRectRenderer::Metrics::Corner& m_corner; qreal m_dx1, m_dy1, m_dx2, m_dy2; }; - class BorderValuesNonUniform + class BorderValuesMulti { public: - inline BorderValuesNonUniform( const QskBoxRenderer::Metrics& metrics ) + inline BorderValuesMulti( const QskRoundedRectRenderer::Metrics& metrics ) : m_uniform( metrics.isRadiusRegular ) { for ( int i = 0; i < 4; i++ ) @@ -225,7 +226,7 @@ namespace class FillValues { public: - inline FillValues( const QskBoxRenderer::Metrics& metrics ) + inline FillValues( const QskRoundedRectRenderer::Metrics& metrics ) { for ( int i = 0; i < 4; i++ ) { @@ -290,7 +291,7 @@ namespace class VRectEllipseIterator { public: - VRectEllipseIterator( const QskBoxRenderer::Metrics& metrics ) + VRectEllipseIterator( const QskRoundedRectRenderer::Metrics& metrics ) : m_metrics( metrics ) , m_values( metrics ) { @@ -308,27 +309,29 @@ namespace m_arcIterator.reset( c[ m_leadingCorner ].stepCount, false ); } - template< class ColorIterator > - inline void setGradientLine( const ColorIterator& it, ColoredLine* line ) + inline bool setGradientLine( qreal value, Color color, ColoredLine* line ) { - const qreal y = it.value(); + const auto& q = m_metrics.innerQuad; + const qreal y = q.top + value * q.height; + 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 ); - line->setLine( left, y, right, y, it.color() ); + line->setLine( left, y, right, y, color ); + return true; } - template< class ColorIterator > - inline void setContourLine( const ColorIterator& it, ColoredLine* line ) + 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, it.colorAt( m_v[ 1 ].y ) ); + m_v[ 1 ].right, m_v[ 1 ].y, color ); } inline qreal value() const { - return m_v[ 1 ].y; + const auto& q = m_metrics.innerQuad; + return ( m_v[ 1 ].y - q.top ) / q.height; } inline bool advance() @@ -380,7 +383,7 @@ namespace } private: - const QskBoxRenderer::Metrics& m_metrics; + const QskRoundedRectRenderer::Metrics& m_metrics; ArcIterator m_arcIterator; int m_leadingCorner; FillValues m_values; @@ -390,7 +393,7 @@ namespace class HRectEllipseIterator { public: - HRectEllipseIterator( const QskBoxRenderer::Metrics& metrics ) + HRectEllipseIterator( const QskRoundedRectRenderer::Metrics& metrics ) : m_metrics( metrics ) , m_values( metrics ) { @@ -408,27 +411,29 @@ namespace m_arcIterator.reset( c[ m_leadingCorner ].stepCount, true ); } - template< class ColorIterator > - inline void setGradientLine( const ColorIterator& it, ColoredLine* line ) + inline bool setGradientLine( qreal value, Color color, ColoredLine* line ) { - const qreal x = it.value(); + const auto& q = m_metrics.innerQuad; + const qreal x = q.left + value * q.width; + 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 ); - line->setLine( x, top, x, bottom, it.color() ); + line->setLine( x, top, x, bottom, color ); + return true; } - template< class ColorIterator > - inline void setContourLine( const ColorIterator& it, ColoredLine* line ) + 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, it.colorAt( m_v[ 1 ].x ) ); + m_v[ 1 ].x, m_v[ 1 ].bottom, color ); } inline qreal value() const { - return m_v[ 1 ].x; + const auto& q = m_metrics.innerQuad; + return ( m_v[ 1 ].x - q.left ) / q.width; } inline bool advance() @@ -479,7 +484,7 @@ namespace } private: - const QskBoxRenderer::Metrics& m_metrics; + const QskRoundedRectRenderer::Metrics& m_metrics; ArcIterator m_arcIterator; int m_leadingCorner; FillValues m_values; @@ -513,7 +518,8 @@ namespace class BorderMapGradient { public: - inline BorderMapGradient( int stepCount, const QskGradient& gradient1, const QskGradient& gradient2 ) + inline BorderMapGradient( int stepCount, + const QskGradient& gradient1, const QskGradient& gradient2 ) : m_stepCount( stepCount ) , m_color1( gradient1.rgbStart() ) , m_color2( gradient2.rgbEnd() ) @@ -541,7 +547,7 @@ namespace class Stroker { public: - inline Stroker( const QskBoxRenderer::Metrics& metrics ) + inline Stroker( const QskRoundedRectRenderer::Metrics& metrics ) : m_metrics( metrics ) { } @@ -576,7 +582,7 @@ namespace auto line = lines + additionalGradientStops( gradient ); { - const auto &stop = stops.first(); + const auto& stop = stops.first(); if ( stop.position() > 0.0 ) setAdditionalLine( x11, y11, x12, y12, dx1, dy1, dx2, dy2, stop, line-- ); @@ -588,7 +594,7 @@ namespace } { - const auto &stop = stops.last(); + const auto& stop = stops.last(); if ( stop.position() < 1.0 ) { @@ -864,7 +870,7 @@ namespace } private: - const QskBoxRenderer::Metrics& m_metrics; + const QskRoundedRectRenderer::Metrics& m_metrics; }; } @@ -874,7 +880,7 @@ static inline Qt::Orientation qskQtOrientation( const QskGradient& gradient ) } static inline int qskFillLineCount( - const QskBoxRenderer::Metrics& metrics, const QskGradient& gradient ) + const QskRoundedRectRenderer::Metrics& metrics, const QskGradient& gradient ) { const int stepCount = metrics.corner[ 0 ].stepCount; @@ -947,7 +953,7 @@ static inline int qskFillLineCount( template< class Line, class BorderMap, class FillMap > static inline void qskRenderLines( - const QskBoxRenderer::Metrics& metrics, + const QskRoundedRectRenderer::Metrics& metrics, Qt::Orientation orientation, Line* borderLines, const BorderMap& borderMapTL, const BorderMap& borderMapTR, const BorderMap& borderMapBL, const BorderMap& borderMapBR, @@ -964,7 +970,7 @@ static inline void qskRenderLines( } else { - Stroker< Line, BorderValuesNonUniform > stroker( metrics ); + Stroker< Line, BorderValuesMulti > stroker( metrics ); stroker.createLines( orientation, borderLines, borderMapTL, borderMapTR, borderMapBL, borderMapBR, fillLines, fillMap ); @@ -973,7 +979,7 @@ static inline void qskRenderLines( template< class Line, class BorderMap, class FillMap > static inline void qskRenderLines( - const QskBoxRenderer::Metrics& metrics, Qt::Orientation orientation, + const QskRoundedRectRenderer::Metrics& metrics, Qt::Orientation orientation, Line* borderLines, const BorderMap& borderMap, Line* fillLines, const FillMap& fillMap ) { @@ -983,7 +989,7 @@ static inline void qskRenderLines( template< class Line, class BorderMap > static inline void qskRenderBorderLines( - const QskBoxRenderer::Metrics& metrics, + const QskRoundedRectRenderer::Metrics& metrics, Qt::Orientation orientation, Line* lines, const BorderMap& borderMapTL, const BorderMap& borderMapTR, const BorderMap& borderMapBL, const BorderMap& borderMapBR ) @@ -994,7 +1000,7 @@ static inline void qskRenderBorderLines( template< class Line, class BorderMap > static inline void qskRenderBorderLines( - const QskBoxRenderer::Metrics& metrics, + const QskRoundedRectRenderer::Metrics& metrics, Qt::Orientation orientation, Line* lines, const BorderMap& borderMap ) { qskRenderBorderLines( metrics, orientation, lines, @@ -1002,14 +1008,14 @@ static inline void qskRenderBorderLines( } template< class Line, class FillMap > -static inline void qskRenderFillLines( const QskBoxRenderer::Metrics& metrics, +static inline void qskRenderFillLines( const QskRoundedRectRenderer::Metrics& metrics, Qt::Orientation orientation, Line* lines, const FillMap& fillMap ) { qskRenderLines( metrics, orientation, static_cast< Line* >( nullptr ), BorderMapNone(), lines, fillMap ); } -static inline void qskRenderBorder( const QskBoxRenderer::Metrics& metrics, +static inline void qskRenderBorder( const QskRoundedRectRenderer::Metrics& metrics, Qt::Orientation orientation, const QskBoxBorderColors& colors, ColoredLine* line ) { if ( colors.isMonochrome() ) @@ -1030,7 +1036,7 @@ static inline void qskRenderBorder( const QskBoxRenderer::Metrics& metrics, } static inline void qskRenderFillRandom( - const QskBoxRenderer::Metrics& metrics, + const QskRoundedRectRenderer::Metrics& metrics, const QskGradient& gradient, ColoredLine* line ) { // here we know that: gradient.stepCount() <= 1 ! @@ -1050,7 +1056,7 @@ static inline void qskRenderFillRandom( } static inline void qskRenderBoxRandom( - const QskBoxRenderer::Metrics& metrics, const QskBoxBorderColors& borderColors, + const QskRoundedRectRenderer::Metrics& metrics, const QskBoxBorderColors& borderColors, const QskGradient& gradient, ColoredLine* fillLine, ColoredLine* borderLine ) { // here we know that: gradient.stepCount() <= 1 ! @@ -1104,11 +1110,9 @@ static inline void qskRenderBoxRandom( } static inline void qskRenderFillOrdered( - const QskBoxRenderer::Metrics& metrics, + const QskRoundedRectRenderer::Metrics& metrics, const QskGradient& gradient, ColoredLine* lines ) { - const auto& r = metrics.innerQuad; - /* The algo for irregular radii at opposite corners is not yet implemented TODO ... @@ -1117,16 +1121,16 @@ static inline void qskRenderFillOrdered( if ( gradient.linearDirection().isHorizontal() ) { HRectEllipseIterator it( metrics ); - QskVertex::fillOrdered( it, r.left, r.right, gradient, lines ); + QskVertex::fillOrdered( it, gradient, lines ); } else { VRectEllipseIterator it( metrics ); - QskVertex::fillOrdered( it, r.top, r.bottom, gradient, lines ); + QskVertex::fillOrdered( it, gradient, lines ); } } -QskBoxRenderer::Metrics::Metrics( const QRectF& rect, +QskRoundedRectRenderer::Metrics::Metrics( const QRectF& rect, const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& border ) : outerQuad( rect ) { @@ -1274,7 +1278,7 @@ QskBoxRenderer::Metrics::Metrics( const QRectF& rect, ( borderRight == borderBottom ); } -void QskBoxRenderer::renderRectellipseBorder( +void QskRoundedRectRenderer::renderRectellipseBorder( const QRectF& rect, const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& border, QSGGeometry& geometry ) { @@ -1293,7 +1297,7 @@ void QskBoxRenderer::renderRectellipseBorder( qskRenderBorderLines( metrics, Qt::Vertical, line, BorderMapNone() ); } -void QskBoxRenderer::renderRectellipseFill( +void QskRoundedRectRenderer::renderRectellipseFill( const QRectF& rect, const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& border, QSGGeometry& geometry ) { @@ -1346,10 +1350,10 @@ void QskBoxRenderer::renderRectellipseFill( auto idx = geometry.indexDataAsUShort(); int i = 0; - + p[i++].set( rect.x() + 0.5 * rect.width(), rect.y() + 0.5 * rect.height() ); - BorderValuesNonUniform v( metrics ); + BorderValuesMulti v( metrics ); { constexpr auto id = TopLeft; @@ -1424,7 +1428,7 @@ void QskBoxRenderer::renderRectellipseFill( #endif } -void QskBoxRenderer::renderRectellipse( const QRectF& rect, +void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect, const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& border, const QskBoxBorderColors& borderColors, const QskGradient& gradient, QSGGeometry& geometry ) @@ -1536,7 +1540,7 @@ void QskBoxRenderer::renderRectellipse( const QRectF& rect, { if ( metrics.isTotallyCropped ) { - renderRectFill( metrics.innerQuad, gradient, line ); + QskRectRenderer::renderFill0( metrics.innerQuad, gradient, line ); } else if ( gradient.linearDirection().isTilted() ) { @@ -1572,7 +1576,7 @@ void QskBoxRenderer::renderRectellipse( const QRectF& rect, { if ( metrics.isTotallyCropped ) { - renderRectFill( metrics.innerQuad, gradient, line ); + QskRectRenderer::renderFill0( metrics.innerQuad, gradient, line ); } else if ( gradient.linearDirection().isTilted() ) { diff --git a/src/nodes/QskRoundedRectRenderer.h b/src/nodes/QskRoundedRectRenderer.h new file mode 100644 index 00000000..dbfa662f --- /dev/null +++ b/src/nodes/QskRoundedRectRenderer.h @@ -0,0 +1,62 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#ifndef QSK_ROUNDED_RECT_RENDERER_H +#define QSK_ROUNDED_RECT_RENDERER_H + +#include "QskVertex.h" + +class QskBoxBorderMetrics; +class QskBoxBorderColors; +class QskBoxShapeMetrics; +class QskGradient; + +class QSGGeometry; +class QRectF; + +namespace QskRoundedRectRenderer +{ + class Metrics + { + public: + Metrics( const QRectF&, const QskBoxShapeMetrics&, const QskBoxBorderMetrics& ); + + QskVertex::Quad outerQuad; + QskVertex::Quad innerQuad; +#if 1 + QskVertex::Quad centerQuad; // to be removed +#endif + + struct Corner + { + bool isCropped; + qreal centerX, centerY; + qreal radiusX, radiusY; + qreal radiusInnerX, radiusInnerY; + + int stepCount; + + } corner[ 4 ]; + + bool isBorderRegular; + bool isRadiusRegular; + bool isTotallyCropped; + }; + + void renderRectellipseFill( const QRectF&, + const QskBoxShapeMetrics&, const QskBoxBorderMetrics&, QSGGeometry& ); + + void renderRectellipseBorder( const QRectF&, + const QskBoxShapeMetrics&, const QskBoxBorderMetrics&, QSGGeometry& ); + + void renderRectellipse( const QRectF&, + const QskBoxShapeMetrics&, const QskBoxBorderMetrics&, + const QskBoxBorderColors&, const QskGradient&, QSGGeometry& ); + + void renderDiagonalFill( const Metrics&, const QskGradient&, + int lineCount, QskVertex::ColoredLine* ); +} + +#endif diff --git a/src/src.pro b/src/src.pro index 8394237c..231dc0b1 100644 --- a/src/src.pro +++ b/src/src.pro @@ -106,6 +106,8 @@ HEADERS += \ nodes/QskBoxFillNode.h \ nodes/QskBoxRectangleNode.h \ nodes/QskBoxRenderer.h \ + nodes/QskRectRenderer.h \ + nodes/QskRoundedRectRenderer.h \ nodes/QskBoxRendererColorMap.h \ nodes/QskBoxShadowNode.h \ nodes/QskColorRamp.h \ @@ -133,8 +135,8 @@ SOURCES += \ nodes/QskBoxFillNode.cpp \ nodes/QskBoxRectangleNode.cpp \ nodes/QskBoxRenderer.cpp \ - nodes/QskBoxRendererRect.cpp \ - nodes/QskBoxRendererEllipse.cpp \ + nodes/QskRectRenderer.cpp \ + nodes/QskRoundedRectRenderer.cpp \ nodes/QskBoxRendererDEllipse.cpp \ nodes/QskBoxShadowNode.cpp \ nodes/QskColorRamp.cpp \