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;
#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();

View File

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

View File

@ -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() )
{
if ( dir.x1() == 0.0 && dir.y1() == 0.0
&& dir.x2() == 1.0 && dir.y2() == 1.0 )
case QskGradient::Stops:
{
// 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;
/*
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;
}
}

View File

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

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
{
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() );
GradientColorIterator colorIt( gradient.stops() );
line = fillOrdered( contourIt, colorIt, line );
}
else
{
TwoColorIterator colorIt( value1, value2,
gradient.rgbStart(), gradient.rgbEnd() );
line = fillOrdered( contourIt, colorIt, line );
}
}
else
{
GradientColorIterator colorIt( value1, value2, gradient.stops() );
GradientColorIterator colorIt( gradient.stops() );
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
*****************************************************************************/
#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

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
*****************************************************************************/
#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 <qmath.h>
#include <qsggeometry.h>
@ -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 )
{
@ -1349,7 +1353,7 @@ void QskBoxRenderer::renderRectellipseFill(
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() )
{

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/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 \