full support of all linear gradient vectors by QskRectRenderer ( = not rounded rectangles ).

This commit is contained in:
Uwe Rathmann 2022-12-28 19:39:08 +01:00
parent 3ce2cea547
commit 8c6f0ffd60
12 changed files with 822 additions and 867 deletions

View File

@ -45,12 +45,20 @@ namespace
} }
QskGradient gradient; QskGradient gradient;
#if 1 #if 0
gradient.setLinearDirection( 0.0, 0.0, 1.0, 1.0 ); gradient.setLinearDirection( 0.0, 1.0, 1.0, 0.0 );
#endif #endif
#if 0 #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.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 #endif
#if 0 #if 0
gradient.setRadialDirection( 0.25, 0.75, 0.25, 0.0 ); gradient.setRadialDirection( 0.25, 0.75, 0.25, 0.0 );
@ -88,7 +96,7 @@ int main( int argc, char** argv )
QskWindow window; QskWindow window;
window.addItem( new MainView() ); window.addItem( new MainView() );
window.resize( 600, 600 ); window.resize( 800, 600 );
window.show(); window.show();
return app.exec(); return app.exec();

View File

@ -44,19 +44,7 @@ static inline QskGradient qskEffectiveGradient( const QskGradient& gradient )
{ {
auto g = gradient.effectiveGradient(); auto g = gradient.effectiveGradient();
if ( g.type() == QskGradient::Linear ) 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
{ {
qWarning() << "QskBoxRectangleNode does not support radial/conic gradients"; qWarning() << "QskBoxRectangleNode does not support radial/conic gradients";
g.setDirection( QskGradient::Linear ); g.setDirection( QskGradient::Linear );

View File

@ -4,6 +4,8 @@
*****************************************************************************/ *****************************************************************************/
#include "QskBoxRenderer.h" #include "QskBoxRenderer.h"
#include "QskRectRenderer.h"
#include "QskRoundedRectRenderer.h"
#include "QskBoxShapeMetrics.h" #include "QskBoxShapeMetrics.h"
#include "QskGradient.h" #include "QskGradient.h"
@ -14,9 +16,9 @@ void QskBoxRenderer::renderBorder(
const QskBoxBorderMetrics& border, QSGGeometry& geometry ) const QskBoxBorderMetrics& border, QSGGeometry& geometry )
{ {
if ( shape.isRectangle() ) if ( shape.isRectangle() )
renderRectBorder( rect, border, geometry ); QskRectRenderer::renderBorder( rect, border, geometry );
else else
renderRectellipseBorder( rect, shape, border, geometry ); QskRoundedRectRenderer::renderRectellipseBorder( rect, shape, border, geometry );
} }
void QskBoxRenderer::renderFill( void QskBoxRenderer::renderFill(
@ -24,9 +26,9 @@ void QskBoxRenderer::renderFill(
const QskBoxBorderMetrics& border, QSGGeometry& geometry ) const QskBoxBorderMetrics& border, QSGGeometry& geometry )
{ {
if ( shape.isRectangle() ) if ( shape.isRectangle() )
renderRectFill( rect, border, geometry ); QskRectRenderer::renderFill( rect, border, geometry );
else else
renderRectellipseFill( rect, shape, border, geometry ); QskRoundedRectRenderer::renderRectellipseFill( rect, shape, border, geometry );
} }
void QskBoxRenderer::renderBox( const QRectF& rect, void QskBoxRenderer::renderBox( const QRectF& rect,
@ -35,39 +37,68 @@ void QskBoxRenderer::renderBox( const QRectF& rect,
QSGGeometry& geometry ) QSGGeometry& geometry )
{ {
if ( shape.isRectangle() ) if ( shape.isRectangle() )
renderRect( rect, border, borderColors, gradient, geometry ); {
QskRectRenderer::renderRect(
rect, border, borderColors, gradient, geometry );
}
else else
renderRectellipse( rect, shape, border, borderColors, gradient, geometry ); {
QskRoundedRectRenderer::renderRectellipse(
rect, shape, border, borderColors, gradient, geometry );
}
} }
bool QskBoxRenderer::isGradientSupported( bool QskBoxRenderer::isGradientSupported(
const QskBoxShapeMetrics&, const QskGradient& gradient ) const QskBoxShapeMetrics& shape, const QskGradient& gradient )
{ {
if ( !gradient.isVisible() || gradient.isMonochrome() if ( !gradient.isVisible() || gradient.isMonochrome() )
|| ( gradient.type() == QskGradient::Stops ) )
{
return true; 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(); case QskGradient::Stops:
if ( dir.isTilted() )
{ {
if ( dir.x1() == 0.0 && dir.y1() == 0.0 // will be rendered as vertical linear gradient
&& dir.x2() == 1.0 && dir.y2() == 1.0 ) return true;
}
case QskGradient::Linear:
{
if ( shape.isRectangle() )
{ {
// rectangles are fully supported
return true; return true;
} }
} else
else {
{ /*
if ( dir.x1() == 0.0 && dir.x2() == 1.0 ) For rounded rectangles we currently support
return true; only the most common use cases. TODO ...
*/
if ( dir.y1() == 0.0 && dir.y2() == 1.0 ) const auto dir = gradient.linearDirection();
return true; 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;
} }
} }

View File

@ -59,15 +59,6 @@ class QSK_EXPORT QskBoxRenderer
}; };
private: 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&, static void renderRectellipseFill( const QRectF&,
const QskBoxShapeMetrics&, const QskBoxBorderMetrics&, QSGGeometry& ); const QskBoxShapeMetrics&, const QskBoxBorderMetrics&, QSGGeometry& );
@ -80,9 +71,6 @@ class QSK_EXPORT QskBoxRenderer
static void renderDiagonalFill( const Metrics&, const QskGradient&, static void renderDiagonalFill( const Metrics&, const QskGradient&,
int lineCount, QskVertex::ColoredLine* ); int lineCount, QskVertex::ColoredLine* );
static void renderRectFill( const QskVertex::Quad&,
const QskGradient&, QskVertex::ColoredLine* );
}; };
#endif #endif

View File

@ -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 class GradientColorIterator : public ColorIterator
{ {
public: public:
inline GradientColorIterator( qreal value1, qreal value2, inline GradientColorIterator( const QskGradientStops& stops )
const QskGradientStops& stops ) : m_stops( stops )
: m_value1( value1 )
, m_value2( value2 )
, m_stops( stops )
{ {
if ( stops.first().position() > 0.0 ) if ( stops.first().position() > 0.0 )
{ {
@ -171,8 +110,8 @@ namespace QskVertex
if ( stops.last().position() < 1.0 ) if ( stops.last().position() < 1.0 )
m_finalIndex++; m_finalIndex++;
m_valueStep1 = value1; m_valueStep1 = 0.0;
m_valueStep2 = valueAt( stops[ m_index ].position() ); m_valueStep2 = stops[ m_index ].position();
m_stepSize = m_valueStep2 - m_valueStep1; m_stepSize = m_valueStep2 - m_valueStep1;
} }
@ -202,14 +141,14 @@ namespace QskVertex
if ( m_index >= m_stops.count() ) if ( m_index >= m_stops.count() )
{ {
m_color2 = m_color1; m_color2 = m_color1;
m_valueStep2 = valueAt( 1.0 ); m_valueStep2 = 1.0;
} }
else else
{ {
const auto& stop = m_stops[ m_index ]; const auto& stop = m_stops[ m_index ];
m_color2 = stop.rgb(); m_color2 = stop.rgb();
m_valueStep2 = valueAt( stop.position() ); m_valueStep2 = stop.position();
} }
m_stepSize = m_valueStep2 - m_valueStep1; m_stepSize = m_valueStep2 - m_valueStep1;
@ -223,12 +162,6 @@ namespace QskVertex
} }
private: 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; const QskGradientStops m_stops;
int m_index, m_finalIndex; int m_index, m_finalIndex;
@ -244,11 +177,14 @@ namespace QskVertex
{ {
while ( !colorIt.isDone() && ( colorIt.value() < contourIt.value() ) ) while ( !colorIt.isDone() && ( colorIt.value() < contourIt.value() ) )
{ {
contourIt.setGradientLine( colorIt, line++ ); if ( contourIt.setGradientLine( colorIt.value(), colorIt.color(), line ) )
line++;
colorIt.advance(); colorIt.advance();
} }
contourIt.setContourLine( colorIt, line++ ); const auto color = colorIt.colorAt( contourIt.value() );
contourIt.setContourLine( color, line++ );
} while ( contourIt.advance() ); } while ( contourIt.advance() );
@ -257,26 +193,16 @@ namespace QskVertex
template< class ContourIterator > template< class ContourIterator >
ColoredLine* fillOrdered( ContourIterator& contourIt, ColoredLine* fillOrdered( ContourIterator& contourIt,
qreal value1, qreal value2, const QskGradient& gradient, ColoredLine* line ) const QskGradient& gradient, ColoredLine* line )
{ {
if ( gradient.stepCount() == 1 ) if ( gradient.stepCount() == 1 )
{ {
if ( value2 == 1.0 && value1 == 0.0 ) GradientColorIterator colorIt( gradient.stops() );
{ line = fillOrdered( contourIt, colorIt, line );
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 );
}
} }
else else
{ {
GradientColorIterator colorIt( value1, value2, gradient.stops() ); GradientColorIterator colorIt( gradient.stops() );
line = fillOrdered( contourIt, colorIt, line ); line = fillOrdered( contourIt, colorIt, line );
} }

View File

@ -3,7 +3,7 @@
* This file may be used under the terms of the QSkinny License, Version 1.0 * 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 "QskBoxRendererColorMap.h"
#include "QskGradient.h" #include "QskGradient.h"
#include "QskVertex.h" #include "QskVertex.h"
@ -44,7 +44,7 @@ namespace
class ValueCurve class ValueCurve
{ {
public: public:
ValueCurve( const QskBoxRenderer::Metrics& m ) ValueCurve( const QskRoundedRectRenderer::Metrics& m )
{ {
/* /*
The slopes of the value line and those for the fill lines. 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, bool isLeading, bool clockwise,
qreal cos, qreal cosStep, qreal sin, qreal sinStep, qreal cos, qreal cosStep, qreal sin, qreal sinStep,
qreal x1, qreal y1, qreal v1, qreal x2, qreal y2, qreal v2 ) 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 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 ) if ( m_isDone )
return; return;
@ -318,7 +320,7 @@ namespace
static constexpr qreal m_eps = 1e-4; static constexpr qreal m_eps = 1e-4;
inline void setCorner( inline void setCorner(
Qt::Corner corner, const QskBoxRenderer::Metrics& metrics ) Qt::Corner corner, const QskRoundedRectRenderer::Metrics& metrics )
{ {
m_corner = corner; m_corner = corner;
const auto& c = metrics.corner[ corner ]; const auto& c = metrics.corner[ corner ];
@ -412,7 +414,7 @@ namespace
class OutlineIterator class OutlineIterator
{ {
public: public:
OutlineIterator( const QskBoxRenderer::Metrics& metrics, OutlineIterator( const QskRoundedRectRenderer::Metrics& metrics,
const ValueCurve& curve, bool clockwise ) const ValueCurve& curve, bool clockwise )
: m_metrics( metrics ) : m_metrics( metrics )
, m_curve( curve ) , m_curve( curve )
@ -549,7 +551,7 @@ namespace
line->setLine( x1, y1, x2, y2, color ); line->setLine( x1, y1, x2, y2, color );
} }
const QskBoxRenderer::Metrics& m_metrics; const QskRoundedRectRenderer::Metrics& m_metrics;
const ValueCurve& m_curve; const ValueCurve& m_curve;
/* /*
@ -564,23 +566,23 @@ namespace
{ {
public: public:
DRectellipseIterator( DRectellipseIterator(
const QskBoxRenderer::Metrics& metrics, const ValueCurve& curve ) const QskRoundedRectRenderer::Metrics& metrics,
const ValueCurve& curve )
: m_left( metrics, curve, false ) : m_left( metrics, curve, false )
, m_right( metrics, curve, true ) , m_right( metrics, curve, true )
{ {
m_next = ( m_left.value() < m_right.value() ) ? &m_left : &m_right; m_next = ( m_left.value() < m_right.value() ) ? &m_left : &m_right;
} }
template< class ColorIterator > inline bool setGradientLine( qreal value, Color color, ColoredLine* line )
inline void setGradientLine( const ColorIterator& it, ColoredLine* line )
{ {
m_next->setLineAt( it.value(), it.color(), line ); m_next->setLineAt( value, color, line );
return true;
} }
template< class ColorIterator > inline void setContourLine( Color color, ColoredLine* line )
inline void setContourLine( const ColorIterator& it, ColoredLine* line )
{ {
m_next->setLine( it.colorAt( m_next->value() ), line ); m_next->setLine( color, line );
} }
inline qreal value() const 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 QskGradient& gradient, int fillLineCount, QskVertex::ColoredLine* lines )
{ {
const ValueCurve curve( metrics ); const ValueCurve curve( metrics );
DRectellipseIterator it( metrics, curve ); 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 There are a couple of reasons, why less points have been rendered

View File

@ -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 );
}

View File

@ -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 );
}
}

View File

@ -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

View File

@ -3,13 +3,14 @@
* This file may be used under the terms of the QSkinny License, Version 1.0 * 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 "QskBoxBorderColors.h"
#include "QskBoxBorderMetrics.h" #include "QskBoxBorderMetrics.h"
#include "QskBoxRendererColorMap.h" #include "QskBoxRendererColorMap.h"
#include "QskBoxShapeMetrics.h" #include "QskBoxShapeMetrics.h"
#include "QskGradientDirection.h" #include "QskGradientDirection.h"
#include "QskRectRenderer.h"
#include <qmath.h> #include <qmath.h>
#include <qsggeometry.h> #include <qsggeometry.h>
@ -109,7 +110,7 @@ namespace
class BorderValuesUniform class BorderValuesUniform
{ {
public: public:
inline BorderValuesUniform( const QskBoxRenderer::Metrics& metrics ) inline BorderValuesUniform( const QskRoundedRectRenderer::Metrics& metrics )
: m_corner( metrics.corner[ 0 ] ) : m_corner( metrics.corner[ 0 ] )
, m_dx1( m_corner.radiusInnerX ) , m_dx1( m_corner.radiusInnerX )
, m_dy1( m_corner.radiusInnerY ) , m_dy1( m_corner.radiusInnerY )
@ -134,14 +135,14 @@ namespace
inline qreal dy2( int ) const { return m_dy2; } inline qreal dy2( int ) const { return m_dy2; }
private: private:
const QskBoxRenderer::Metrics::Corner& m_corner; const QskRoundedRectRenderer::Metrics::Corner& m_corner;
qreal m_dx1, m_dy1, m_dx2, m_dy2; qreal m_dx1, m_dy1, m_dx2, m_dy2;
}; };
class BorderValuesNonUniform class BorderValuesMulti
{ {
public: public:
inline BorderValuesNonUniform( const QskBoxRenderer::Metrics& metrics ) inline BorderValuesMulti( const QskRoundedRectRenderer::Metrics& metrics )
: m_uniform( metrics.isRadiusRegular ) : m_uniform( metrics.isRadiusRegular )
{ {
for ( int i = 0; i < 4; i++ ) for ( int i = 0; i < 4; i++ )
@ -225,7 +226,7 @@ namespace
class FillValues class FillValues
{ {
public: public:
inline FillValues( const QskBoxRenderer::Metrics& metrics ) inline FillValues( const QskRoundedRectRenderer::Metrics& metrics )
{ {
for ( int i = 0; i < 4; i++ ) for ( int i = 0; i < 4; i++ )
{ {
@ -290,7 +291,7 @@ namespace
class VRectEllipseIterator class VRectEllipseIterator
{ {
public: public:
VRectEllipseIterator( const QskBoxRenderer::Metrics& metrics ) VRectEllipseIterator( const QskRoundedRectRenderer::Metrics& metrics )
: m_metrics( metrics ) : m_metrics( metrics )
, m_values( metrics ) , m_values( metrics )
{ {
@ -308,27 +309,29 @@ namespace
m_arcIterator.reset( c[ m_leadingCorner ].stepCount, false ); m_arcIterator.reset( c[ m_leadingCorner ].stepCount, false );
} }
template< class ColorIterator > inline bool setGradientLine( qreal value, Color color, ColoredLine* line )
inline void setGradientLine( const ColorIterator& it, 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 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 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 ); 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( Color color, ColoredLine* line )
inline void setContourLine( const ColorIterator& it, ColoredLine* line )
{ {
line->setLine( m_v[ 1 ].left, m_v[ 1 ].y, 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 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() inline bool advance()
@ -380,7 +383,7 @@ namespace
} }
private: private:
const QskBoxRenderer::Metrics& m_metrics; const QskRoundedRectRenderer::Metrics& m_metrics;
ArcIterator m_arcIterator; ArcIterator m_arcIterator;
int m_leadingCorner; int m_leadingCorner;
FillValues m_values; FillValues m_values;
@ -390,7 +393,7 @@ namespace
class HRectEllipseIterator class HRectEllipseIterator
{ {
public: public:
HRectEllipseIterator( const QskBoxRenderer::Metrics& metrics ) HRectEllipseIterator( const QskRoundedRectRenderer::Metrics& metrics )
: m_metrics( metrics ) : m_metrics( metrics )
, m_values( metrics ) , m_values( metrics )
{ {
@ -408,27 +411,29 @@ namespace
m_arcIterator.reset( c[ m_leadingCorner ].stepCount, true ); m_arcIterator.reset( c[ m_leadingCorner ].stepCount, true );
} }
template< class ColorIterator > inline bool setGradientLine( qreal value, Color color, ColoredLine* line )
inline void setGradientLine( const ColorIterator& it, 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 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 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 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( Color color, ColoredLine* line )
inline void setContourLine( const ColorIterator& it, ColoredLine* line )
{ {
line->setLine( m_v[ 1 ].x, m_v[ 1 ].top, 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 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() inline bool advance()
@ -479,7 +484,7 @@ namespace
} }
private: private:
const QskBoxRenderer::Metrics& m_metrics; const QskRoundedRectRenderer::Metrics& m_metrics;
ArcIterator m_arcIterator; ArcIterator m_arcIterator;
int m_leadingCorner; int m_leadingCorner;
FillValues m_values; FillValues m_values;
@ -513,7 +518,8 @@ namespace
class BorderMapGradient class BorderMapGradient
{ {
public: 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_stepCount( stepCount )
, m_color1( gradient1.rgbStart() ) , m_color1( gradient1.rgbStart() )
, m_color2( gradient2.rgbEnd() ) , m_color2( gradient2.rgbEnd() )
@ -541,7 +547,7 @@ namespace
class Stroker class Stroker
{ {
public: public:
inline Stroker( const QskBoxRenderer::Metrics& metrics ) inline Stroker( const QskRoundedRectRenderer::Metrics& metrics )
: m_metrics( metrics ) : m_metrics( metrics )
{ {
} }
@ -576,7 +582,7 @@ namespace
auto line = lines + additionalGradientStops( gradient ); auto line = lines + additionalGradientStops( gradient );
{ {
const auto &stop = stops.first(); const auto& stop = stops.first();
if ( stop.position() > 0.0 ) if ( stop.position() > 0.0 )
setAdditionalLine( x11, y11, x12, y12, dx1, dy1, dx2, dy2, stop, line-- ); 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 ) if ( stop.position() < 1.0 )
{ {
@ -864,7 +870,7 @@ namespace
} }
private: 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( 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; const int stepCount = metrics.corner[ 0 ].stepCount;
@ -947,7 +953,7 @@ static inline int qskFillLineCount(
template< class Line, class BorderMap, class FillMap > template< class Line, class BorderMap, class FillMap >
static inline void qskRenderLines( static inline void qskRenderLines(
const QskBoxRenderer::Metrics& metrics, const QskRoundedRectRenderer::Metrics& metrics,
Qt::Orientation orientation, Line* borderLines, Qt::Orientation orientation, Line* borderLines,
const BorderMap& borderMapTL, const BorderMap& borderMapTR, const BorderMap& borderMapTL, const BorderMap& borderMapTR,
const BorderMap& borderMapBL, const BorderMap& borderMapBR, const BorderMap& borderMapBL, const BorderMap& borderMapBR,
@ -964,7 +970,7 @@ static inline void qskRenderLines(
} }
else else
{ {
Stroker< Line, BorderValuesNonUniform > stroker( metrics ); Stroker< Line, BorderValuesMulti > stroker( metrics );
stroker.createLines( orientation, borderLines, stroker.createLines( orientation, borderLines,
borderMapTL, borderMapTR, borderMapBL, borderMapBR, borderMapTL, borderMapTR, borderMapBL, borderMapBR,
fillLines, fillMap ); fillLines, fillMap );
@ -973,7 +979,7 @@ static inline void qskRenderLines(
template< class Line, class BorderMap, class FillMap > template< class Line, class BorderMap, class FillMap >
static inline void qskRenderLines( 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, Line* borderLines, const BorderMap& borderMap, Line* fillLines,
const FillMap& fillMap ) const FillMap& fillMap )
{ {
@ -983,7 +989,7 @@ static inline void qskRenderLines(
template< class Line, class BorderMap > template< class Line, class BorderMap >
static inline void qskRenderBorderLines( static inline void qskRenderBorderLines(
const QskBoxRenderer::Metrics& metrics, const QskRoundedRectRenderer::Metrics& metrics,
Qt::Orientation orientation, Line* lines, Qt::Orientation orientation, Line* lines,
const BorderMap& borderMapTL, const BorderMap& borderMapTR, const BorderMap& borderMapTL, const BorderMap& borderMapTR,
const BorderMap& borderMapBL, const BorderMap& borderMapBR ) const BorderMap& borderMapBL, const BorderMap& borderMapBR )
@ -994,7 +1000,7 @@ static inline void qskRenderBorderLines(
template< class Line, class BorderMap > template< class Line, class BorderMap >
static inline void qskRenderBorderLines( static inline void qskRenderBorderLines(
const QskBoxRenderer::Metrics& metrics, const QskRoundedRectRenderer::Metrics& metrics,
Qt::Orientation orientation, Line* lines, const BorderMap& borderMap ) Qt::Orientation orientation, Line* lines, const BorderMap& borderMap )
{ {
qskRenderBorderLines( metrics, orientation, lines, qskRenderBorderLines( metrics, orientation, lines,
@ -1002,14 +1008,14 @@ static inline void qskRenderBorderLines(
} }
template< class Line, class FillMap > 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 ) Qt::Orientation orientation, Line* lines, const FillMap& fillMap )
{ {
qskRenderLines( metrics, orientation, qskRenderLines( metrics, orientation,
static_cast< Line* >( nullptr ), BorderMapNone(), lines, fillMap ); 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 ) Qt::Orientation orientation, const QskBoxBorderColors& colors, ColoredLine* line )
{ {
if ( colors.isMonochrome() ) if ( colors.isMonochrome() )
@ -1030,7 +1036,7 @@ static inline void qskRenderBorder( const QskBoxRenderer::Metrics& metrics,
} }
static inline void qskRenderFillRandom( static inline void qskRenderFillRandom(
const QskBoxRenderer::Metrics& metrics, const QskRoundedRectRenderer::Metrics& metrics,
const QskGradient& gradient, ColoredLine* line ) const QskGradient& gradient, ColoredLine* line )
{ {
// here we know that: gradient.stepCount() <= 1 ! // here we know that: gradient.stepCount() <= 1 !
@ -1050,7 +1056,7 @@ static inline void qskRenderFillRandom(
} }
static inline void qskRenderBoxRandom( 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 ) const QskGradient& gradient, ColoredLine* fillLine, ColoredLine* borderLine )
{ {
// here we know that: gradient.stepCount() <= 1 ! // here we know that: gradient.stepCount() <= 1 !
@ -1104,11 +1110,9 @@ static inline void qskRenderBoxRandom(
} }
static inline void qskRenderFillOrdered( static inline void qskRenderFillOrdered(
const QskBoxRenderer::Metrics& metrics, const QskRoundedRectRenderer::Metrics& metrics,
const QskGradient& gradient, ColoredLine* lines ) const QskGradient& gradient, ColoredLine* lines )
{ {
const auto& r = metrics.innerQuad;
/* /*
The algo for irregular radii at opposite corners is not yet The algo for irregular radii at opposite corners is not yet
implemented TODO ... implemented TODO ...
@ -1117,16 +1121,16 @@ static inline void qskRenderFillOrdered(
if ( gradient.linearDirection().isHorizontal() ) if ( gradient.linearDirection().isHorizontal() )
{ {
HRectEllipseIterator it( metrics ); HRectEllipseIterator it( metrics );
QskVertex::fillOrdered( it, r.left, r.right, gradient, lines ); QskVertex::fillOrdered( it, gradient, lines );
} }
else else
{ {
VRectEllipseIterator it( metrics ); 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 ) const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& border )
: outerQuad( rect ) : outerQuad( rect )
{ {
@ -1274,7 +1278,7 @@ QskBoxRenderer::Metrics::Metrics( const QRectF& rect,
( borderRight == borderBottom ); ( borderRight == borderBottom );
} }
void QskBoxRenderer::renderRectellipseBorder( void QskRoundedRectRenderer::renderRectellipseBorder(
const QRectF& rect, const QskBoxShapeMetrics& shape, const QRectF& rect, const QskBoxShapeMetrics& shape,
const QskBoxBorderMetrics& border, QSGGeometry& geometry ) const QskBoxBorderMetrics& border, QSGGeometry& geometry )
{ {
@ -1293,7 +1297,7 @@ void QskBoxRenderer::renderRectellipseBorder(
qskRenderBorderLines( metrics, Qt::Vertical, line, BorderMapNone() ); qskRenderBorderLines( metrics, Qt::Vertical, line, BorderMapNone() );
} }
void QskBoxRenderer::renderRectellipseFill( void QskRoundedRectRenderer::renderRectellipseFill(
const QRectF& rect, const QskBoxShapeMetrics& shape, const QRectF& rect, const QskBoxShapeMetrics& shape,
const QskBoxBorderMetrics& border, QSGGeometry& geometry ) const QskBoxBorderMetrics& border, QSGGeometry& geometry )
{ {
@ -1346,10 +1350,10 @@ void QskBoxRenderer::renderRectellipseFill(
auto idx = geometry.indexDataAsUShort(); auto idx = geometry.indexDataAsUShort();
int i = 0; int i = 0;
p[i++].set( rect.x() + 0.5 * rect.width(), rect.y() + 0.5 * rect.height() ); 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; constexpr auto id = TopLeft;
@ -1424,7 +1428,7 @@ void QskBoxRenderer::renderRectellipseFill(
#endif #endif
} }
void QskBoxRenderer::renderRectellipse( const QRectF& rect, void QskRoundedRectRenderer::renderRectellipse( const QRectF& rect,
const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& border, const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& border,
const QskBoxBorderColors& borderColors, const QskGradient& gradient, const QskBoxBorderColors& borderColors, const QskGradient& gradient,
QSGGeometry& geometry ) QSGGeometry& geometry )
@ -1536,7 +1540,7 @@ void QskBoxRenderer::renderRectellipse( const QRectF& rect,
{ {
if ( metrics.isTotallyCropped ) if ( metrics.isTotallyCropped )
{ {
renderRectFill( metrics.innerQuad, gradient, line ); QskRectRenderer::renderFill0( metrics.innerQuad, gradient, line );
} }
else if ( gradient.linearDirection().isTilted() ) else if ( gradient.linearDirection().isTilted() )
{ {
@ -1572,7 +1576,7 @@ void QskBoxRenderer::renderRectellipse( const QRectF& rect,
{ {
if ( metrics.isTotallyCropped ) if ( metrics.isTotallyCropped )
{ {
renderRectFill( metrics.innerQuad, gradient, line ); QskRectRenderer::renderFill0( metrics.innerQuad, gradient, line );
} }
else if ( gradient.linearDirection().isTilted() ) else if ( gradient.linearDirection().isTilted() )
{ {

View File

@ -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

View File

@ -106,6 +106,8 @@ HEADERS += \
nodes/QskBoxFillNode.h \ nodes/QskBoxFillNode.h \
nodes/QskBoxRectangleNode.h \ nodes/QskBoxRectangleNode.h \
nodes/QskBoxRenderer.h \ nodes/QskBoxRenderer.h \
nodes/QskRectRenderer.h \
nodes/QskRoundedRectRenderer.h \
nodes/QskBoxRendererColorMap.h \ nodes/QskBoxRendererColorMap.h \
nodes/QskBoxShadowNode.h \ nodes/QskBoxShadowNode.h \
nodes/QskColorRamp.h \ nodes/QskColorRamp.h \
@ -133,8 +135,8 @@ SOURCES += \
nodes/QskBoxFillNode.cpp \ nodes/QskBoxFillNode.cpp \
nodes/QskBoxRectangleNode.cpp \ nodes/QskBoxRectangleNode.cpp \
nodes/QskBoxRenderer.cpp \ nodes/QskBoxRenderer.cpp \
nodes/QskBoxRendererRect.cpp \ nodes/QskRectRenderer.cpp \
nodes/QskBoxRendererEllipse.cpp \ nodes/QskRoundedRectRenderer.cpp \
nodes/QskBoxRendererDEllipse.cpp \ nodes/QskBoxRendererDEllipse.cpp \
nodes/QskBoxShadowNode.cpp \ nodes/QskBoxShadowNode.cpp \
nodes/QskColorRamp.cpp \ nodes/QskColorRamp.cpp \