1321 lines
41 KiB
C++
1321 lines
41 KiB
C++
/******************************************************************************
|
|
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
|
* This file may be used under the terms of the QSkinny License, Version 1.0
|
|
*****************************************************************************/
|
|
|
|
#include "QskBoxRenderer.h"
|
|
#include "QskGradient.h"
|
|
|
|
#include "QskBoxRendererColorMap.h"
|
|
#include "QskBoxBorderMetrics.h"
|
|
#include "QskBoxBorderColors.h"
|
|
#include "QskBoxShapeMetrics.h"
|
|
|
|
#include <qsggeometry.h>
|
|
#include <qmath.h>
|
|
|
|
using namespace QskVertex;
|
|
|
|
namespace
|
|
{
|
|
enum
|
|
{
|
|
TopLeft = Qt::TopLeftCorner,
|
|
TopRight = Qt::TopRightCorner,
|
|
BottomLeft = Qt::BottomLeftCorner,
|
|
BottomRight = Qt::BottomRightCorner
|
|
};
|
|
|
|
class ArcIterator
|
|
{
|
|
public:
|
|
inline ArcIterator() = default;
|
|
|
|
inline ArcIterator( int stepCount, bool inverted = false )
|
|
{
|
|
reset( stepCount, inverted );
|
|
}
|
|
|
|
void reset( int stepCount, bool inverted )
|
|
{
|
|
m_inverted = inverted;
|
|
|
|
if ( inverted )
|
|
{
|
|
m_cos = 1.0;
|
|
m_sin = 0.0;
|
|
}
|
|
else
|
|
{
|
|
m_cos = 0.0;
|
|
m_sin = 1.0;
|
|
}
|
|
|
|
m_stepIndex = 0;
|
|
m_stepCount = stepCount;
|
|
|
|
const double angleStep = M_PI_2 / stepCount;
|
|
m_cosStep = qFastCos( angleStep );
|
|
m_sinStep = qFastSin( angleStep );
|
|
}
|
|
|
|
|
|
inline bool isInverted() const { return m_inverted; }
|
|
|
|
inline double cos() const { return m_cos; }
|
|
inline double sin() const { return m_inverted ? -m_sin : m_sin; }
|
|
|
|
inline int step() const { return m_stepIndex; }
|
|
inline int stepCount() const { return m_stepCount; }
|
|
inline bool isDone() const { return m_stepIndex > m_stepCount; }
|
|
|
|
inline void increment()
|
|
{
|
|
const double cos0 = m_cos;
|
|
|
|
m_cos = m_cos * m_cosStep + m_sin * m_sinStep;
|
|
m_sin = m_sin * m_cosStep - cos0 * m_sinStep;
|
|
|
|
++m_stepIndex;
|
|
}
|
|
|
|
inline void operator++() { increment(); }
|
|
|
|
static int segmentHint( double radius )
|
|
{
|
|
const double arcLength = radius * M_PI_2;
|
|
return qBound( 3, qCeil( arcLength / 3.0 ), 18 ); // every 3 pixels
|
|
}
|
|
|
|
private:
|
|
double m_cos;
|
|
double m_sin;
|
|
|
|
int m_stepIndex;
|
|
double m_cosStep;
|
|
double m_sinStep;
|
|
|
|
int m_stepCount;
|
|
bool m_inverted;
|
|
};
|
|
}
|
|
|
|
namespace
|
|
{
|
|
class BorderValuesUniform
|
|
{
|
|
public:
|
|
inline BorderValuesUniform( const QskBoxRenderer::Metrics& metrics ):
|
|
m_corner( metrics.corner[0] ),
|
|
m_dx1( m_corner.radiusInnerX ),
|
|
m_dy1( m_corner.radiusInnerY )
|
|
{
|
|
}
|
|
|
|
inline void setAngle( qreal cos, qreal sin )
|
|
{
|
|
if ( !m_corner.isCropped )
|
|
{
|
|
m_dx1 = cos * m_corner.radiusInnerX;;
|
|
m_dy1 = sin * m_corner.radiusInnerY;
|
|
}
|
|
|
|
m_dx2 = cos * m_corner.radiusX;
|
|
m_dy2 = sin * m_corner.radiusY;
|
|
}
|
|
|
|
inline qreal dx1( int ) const { return m_dx1; }
|
|
inline qreal dy1( int ) const { return m_dy1; }
|
|
inline qreal dx2( int ) const { return m_dx2; }
|
|
inline qreal dy2( int ) const { return m_dy2; }
|
|
|
|
private:
|
|
const QskBoxRenderer::Metrics::Corner& m_corner;
|
|
qreal m_dx1, m_dy1, m_dx2, m_dy2;
|
|
};
|
|
|
|
class BorderValues
|
|
{
|
|
public:
|
|
inline BorderValues( const QskBoxRenderer::Metrics& metrics ):
|
|
m_uniform( metrics.isRadiusRegular )
|
|
{
|
|
for ( int i = 0; i < 4; i++ )
|
|
{
|
|
const auto& c = metrics.corner[i];
|
|
auto& v = m_inner[i];
|
|
|
|
if ( c.radiusInnerX >= 0.0 )
|
|
{
|
|
v.x0 = 0.0;
|
|
v.rx = c.radiusInnerX;
|
|
}
|
|
else
|
|
{
|
|
v.x0 = c.radiusInnerX;
|
|
v.rx = 0.0;
|
|
}
|
|
|
|
if ( c.radiusInnerY >= 0.0 )
|
|
{
|
|
v.y0 = 0.0;
|
|
v.ry = c.radiusInnerY;
|
|
}
|
|
else
|
|
{
|
|
v.y0 = c.radiusInnerY;
|
|
v.ry = 0.0;
|
|
}
|
|
|
|
m_outer[i].x0 = m_outer[i].y0 = 0.0;
|
|
m_outer[i].rx = c.radiusX;
|
|
m_outer[i].ry = c.radiusY;
|
|
}
|
|
}
|
|
|
|
inline void setAngle( qreal cos, qreal sin )
|
|
{
|
|
m_inner[0].setAngle( cos, sin );
|
|
m_inner[1].setAngle( cos, sin );
|
|
m_inner[2].setAngle( cos, sin );
|
|
m_inner[3].setAngle( cos, sin );
|
|
|
|
m_outer[0].setAngle( cos, sin );
|
|
if ( !m_uniform )
|
|
{
|
|
m_outer[1].setAngle( cos, sin );
|
|
m_outer[2].setAngle( cos, sin );
|
|
m_outer[3].setAngle( cos, sin );
|
|
}
|
|
}
|
|
|
|
inline qreal dx1( int pos ) const { return m_inner[pos].dx; }
|
|
inline qreal dy1( int pos ) const { return m_inner[pos].dy; }
|
|
inline qreal dx2( int pos ) const
|
|
{ return m_uniform ? m_outer[0].dx : m_outer[pos].dx; }
|
|
inline qreal dy2( int pos ) const
|
|
{ return m_uniform ? m_outer[0].dy : m_outer[pos].dy; }
|
|
|
|
private:
|
|
bool m_uniform;
|
|
class Values
|
|
{
|
|
public:
|
|
inline void setAngle( qreal cos, qreal sin )
|
|
{
|
|
dx = x0 + cos * rx;
|
|
dy = y0 + sin * ry;
|
|
}
|
|
|
|
qreal dx, dy;
|
|
qreal x0, y0, rx, ry;
|
|
};
|
|
|
|
Values m_inner[4];
|
|
Values m_outer[4];
|
|
};
|
|
|
|
class FillValues
|
|
{
|
|
public:
|
|
inline FillValues( const QskBoxRenderer::Metrics& metrics )
|
|
{
|
|
for ( int i = 0; i < 4; i++ )
|
|
{
|
|
const auto& c = metrics.corner[i];
|
|
auto& v = m_inner[i];
|
|
|
|
if ( c.radiusInnerX >= 0.0 )
|
|
{
|
|
v.x0 = 0.0;
|
|
v.rx = c.radiusInnerX;
|
|
}
|
|
else
|
|
{
|
|
v.x0 = c.radiusInnerX;
|
|
v.rx = 0.0;
|
|
}
|
|
|
|
if ( c.radiusInnerY >= 0.0 )
|
|
{
|
|
v.y0 = 0.0;
|
|
v.ry = c.radiusInnerY;
|
|
}
|
|
else
|
|
{
|
|
v.y0 = c.radiusInnerY;
|
|
v.ry = 0.0;
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void setAngle( qreal cos, qreal sin )
|
|
{
|
|
m_inner[0].setAngle( cos, sin );
|
|
m_inner[1].setAngle( cos, sin );
|
|
m_inner[2].setAngle( cos, sin );
|
|
m_inner[3].setAngle( cos, sin );
|
|
}
|
|
|
|
inline qreal dx( int pos ) const { return m_inner[pos].dx; }
|
|
inline qreal dy( int pos ) const { return m_inner[pos].dy; }
|
|
|
|
private:
|
|
class Values
|
|
{
|
|
public:
|
|
inline void setAngle( qreal cos, qreal sin )
|
|
{
|
|
dx = x0 + cos * rx;
|
|
dy = y0 + sin * ry;
|
|
}
|
|
|
|
qreal dx, dy;
|
|
qreal x0, y0, rx, ry;
|
|
};
|
|
|
|
Values m_inner[4];
|
|
};
|
|
}
|
|
|
|
namespace
|
|
{
|
|
class VRectEllipseIterator
|
|
{
|
|
public:
|
|
VRectEllipseIterator( const QskBoxRenderer::Metrics& metrics ):
|
|
m_metrics( metrics ),
|
|
m_values( metrics )
|
|
{
|
|
const auto& c = metrics.corner;
|
|
|
|
m_v[0].left = c[TopLeft].centerX;
|
|
m_v[0].right = c[TopRight].centerX;
|
|
m_v[0].y = metrics.innerQuad.top;
|
|
|
|
m_v[1] = m_v[0];
|
|
|
|
m_leadingCorner = ( c[TopLeft].stepCount >= c[TopRight].stepCount )
|
|
? TopLeft : TopRight;
|
|
|
|
m_arcIterator.reset( c[m_leadingCorner].stepCount, false );
|
|
}
|
|
|
|
template< class ColorIterator >
|
|
inline void setGradientLine( const ColorIterator& it, ColoredLine* line )
|
|
{
|
|
const qreal y = it.value();
|
|
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() );
|
|
}
|
|
|
|
template< class ColorIterator >
|
|
inline void setContourLine( const ColorIterator& it, 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 ) );
|
|
}
|
|
|
|
inline qreal value() const
|
|
{
|
|
return m_v[1].y;
|
|
}
|
|
|
|
inline bool advance()
|
|
{
|
|
const auto& centerQuad = m_metrics.centerQuad;
|
|
const auto& c = m_metrics.corner;
|
|
|
|
if ( m_arcIterator.step() == m_arcIterator.stepCount() )
|
|
{
|
|
if ( m_arcIterator.isInverted() )
|
|
return false;
|
|
|
|
m_leadingCorner = ( c[BottomLeft].stepCount >= c[BottomRight].stepCount )
|
|
? BottomLeft : BottomRight;
|
|
|
|
m_arcIterator.reset( c[m_leadingCorner].stepCount, true );
|
|
|
|
if ( centerQuad.top < centerQuad.bottom )
|
|
{
|
|
m_v[0] = m_v[1];
|
|
|
|
m_v[1].left = m_metrics.innerQuad.left;
|
|
m_v[1].right = m_metrics.innerQuad.right;
|
|
m_v[1].y = centerQuad.bottom;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
m_arcIterator.increment();
|
|
m_values.setAngle( m_arcIterator.cos(), m_arcIterator.sin() );
|
|
|
|
m_v[0] = m_v[1];
|
|
|
|
if ( m_arcIterator.isInverted() )
|
|
{
|
|
m_v[1].left = c[BottomLeft].centerX - m_values.dx( BottomLeft );
|
|
m_v[1].right = c[BottomRight].centerX + m_values.dx( BottomRight );
|
|
m_v[1].y = c[ m_leadingCorner ].centerY + m_values.dy( m_leadingCorner );
|
|
}
|
|
else
|
|
{
|
|
m_v[1].left = c[TopLeft].centerX - m_values.dx( TopLeft );
|
|
m_v[1].right = c[TopRight].centerX + m_values.dx( TopRight );
|
|
m_v[1].y = c[ m_leadingCorner ].centerY - m_values.dy( m_leadingCorner );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
const QskBoxRenderer::Metrics& m_metrics;
|
|
ArcIterator m_arcIterator;
|
|
int m_leadingCorner;
|
|
FillValues m_values;
|
|
struct { qreal left, right, y; } m_v[2];
|
|
};
|
|
|
|
class HRectEllipseIterator
|
|
{
|
|
public:
|
|
HRectEllipseIterator( const QskBoxRenderer::Metrics& metrics ):
|
|
m_metrics( metrics ),
|
|
m_values( metrics )
|
|
{
|
|
const auto& c = metrics.corner;
|
|
|
|
m_v[0].top = c[TopLeft].centerY;
|
|
m_v[0].bottom = c[BottomLeft].centerY;
|
|
m_v[0].x = metrics.innerQuad.left;
|
|
|
|
m_v[1] = m_v[0];
|
|
|
|
m_leadingCorner = ( c[TopLeft].stepCount >= c[BottomLeft].stepCount )
|
|
? TopLeft : BottomLeft;
|
|
|
|
m_arcIterator.reset( c[m_leadingCorner].stepCount, true );
|
|
}
|
|
|
|
template< class ColorIterator >
|
|
inline void setGradientLine( const ColorIterator& it, ColoredLine* line )
|
|
{
|
|
const qreal x = it.value();
|
|
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() );
|
|
}
|
|
|
|
template< class ColorIterator >
|
|
inline void setContourLine( const ColorIterator& it, 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 ) );
|
|
}
|
|
|
|
inline qreal value() const
|
|
{
|
|
return m_v[1].x;
|
|
}
|
|
|
|
inline bool advance()
|
|
{
|
|
const auto& centerQuad = m_metrics.centerQuad;
|
|
const auto& c = m_metrics.corner;
|
|
|
|
if ( m_arcIterator.step() == m_arcIterator.stepCount() )
|
|
{
|
|
if ( !m_arcIterator.isInverted() )
|
|
return false;
|
|
|
|
m_leadingCorner = ( c[TopRight].stepCount >= c[BottomRight].stepCount )
|
|
? TopRight : BottomRight;
|
|
|
|
m_arcIterator.reset( c[m_leadingCorner].stepCount, false );
|
|
if ( centerQuad.left < centerQuad.right )
|
|
{
|
|
m_v[0] = m_v[1];
|
|
|
|
m_v[1].top = m_metrics.innerQuad.top;
|
|
m_v[1].bottom = m_metrics.innerQuad.bottom;
|
|
m_v[1].x = centerQuad.right;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
m_arcIterator.increment();
|
|
m_values.setAngle( m_arcIterator.cos(), m_arcIterator.sin() );
|
|
|
|
m_v[0] = m_v[1];
|
|
|
|
if ( m_arcIterator.isInverted() )
|
|
{
|
|
m_v[1].top = c[TopLeft].centerY - m_values.dy( TopLeft );
|
|
m_v[1].bottom = c[BottomLeft].centerY + m_values.dy( BottomLeft );
|
|
m_v[1].x = c[m_leadingCorner].centerX - m_values.dx( m_leadingCorner );
|
|
}
|
|
else
|
|
{
|
|
m_v[1].top = c[TopRight].centerY - m_values.dy( TopRight );
|
|
m_v[1].bottom = c[BottomRight].centerY + m_values.dy( BottomRight );
|
|
m_v[1].x = c[m_leadingCorner].centerX + m_values.dx( m_leadingCorner );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
const QskBoxRenderer::Metrics& m_metrics;
|
|
ArcIterator m_arcIterator;
|
|
int m_leadingCorner;
|
|
FillValues m_values;
|
|
struct { qreal top, bottom, x; } m_v[2];
|
|
};
|
|
}
|
|
|
|
namespace
|
|
{
|
|
class BorderMapNone
|
|
{
|
|
public:
|
|
static inline constexpr Color colorAt( int ) { return Color(); }
|
|
};
|
|
|
|
class BorderMapSolid
|
|
{
|
|
public:
|
|
inline BorderMapSolid( QRgb rgb ):
|
|
m_color( rgb )
|
|
{
|
|
}
|
|
|
|
inline Color colorAt( int ) const { return m_color; }
|
|
|
|
const Color m_color;
|
|
};
|
|
|
|
class BorderMapGradient
|
|
{
|
|
public:
|
|
inline BorderMapGradient( int stepCount, QRgb rgb1, QRgb rgb2 ):
|
|
m_stepCount( stepCount ),
|
|
m_color1( rgb1 ),
|
|
m_color2( rgb2 )
|
|
{
|
|
}
|
|
|
|
inline Color colorAt( int step ) const
|
|
{
|
|
return m_color1.interpolatedTo( m_color2, step / m_stepCount );
|
|
}
|
|
|
|
private:
|
|
const qreal m_stepCount;
|
|
const Color m_color1, m_color2;
|
|
};
|
|
|
|
template< class Line, class BorderValues >
|
|
class Stroker
|
|
{
|
|
public:
|
|
inline Stroker( const QskBoxRenderer::Metrics& metrics ):
|
|
m_metrics( metrics )
|
|
{
|
|
}
|
|
|
|
template< class BorderMap, class FillMap >
|
|
inline void createLines( Qt::Orientation orientation, Line* borderLines,
|
|
const BorderMap& borderMapTL, const BorderMap& borderMapTR,
|
|
const BorderMap& borderMapBL, const BorderMap& borderMapBR,
|
|
Line* fillLines, FillMap& fillMap )
|
|
{
|
|
const auto& c = m_metrics.corner;
|
|
#if 1
|
|
// TODO ...
|
|
const int stepCount = c[0].stepCount;
|
|
#endif
|
|
|
|
Line* linesBR, * linesTR, * linesTL, * linesBL;
|
|
linesBR = linesTR = linesTL = linesBL = nullptr;
|
|
|
|
const int numCornerLines = stepCount + 1;
|
|
int numFillLines = fillLines ? 2 * numCornerLines : 0;
|
|
|
|
if ( orientation == Qt::Vertical )
|
|
{
|
|
if ( borderLines )
|
|
{
|
|
linesBR = borderLines;
|
|
linesTR = linesBR + numCornerLines;
|
|
linesTL = linesTR + numCornerLines;
|
|
linesBL = linesTL + numCornerLines;
|
|
}
|
|
|
|
if ( fillLines )
|
|
{
|
|
if ( m_metrics.centerQuad.top >= m_metrics.centerQuad.bottom )
|
|
numFillLines--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( borderLines )
|
|
{
|
|
linesTR = borderLines + 1;
|
|
linesTL = linesTR + numCornerLines;
|
|
linesBL = linesTL + numCornerLines;
|
|
linesBR = linesBL + numCornerLines;
|
|
}
|
|
|
|
if ( fillLines )
|
|
{
|
|
if ( m_metrics.centerQuad.left >= m_metrics.centerQuad.right )
|
|
numFillLines--;
|
|
}
|
|
}
|
|
|
|
BorderValues v( m_metrics );
|
|
|
|
/*
|
|
It would be possible to run over [0, 0.5 * M_PI_2]
|
|
and create 8 values ( instead of 4 ) in each step. TODO ...
|
|
*/
|
|
for ( ArcIterator it( stepCount, false ); !it.isDone(); ++it )
|
|
{
|
|
v.setAngle( it.cos(), it.sin() );
|
|
|
|
if ( borderLines )
|
|
{
|
|
const int j = it.step();
|
|
const int k = numCornerLines - it.step() - 1;
|
|
|
|
{
|
|
constexpr auto corner = TopLeft;
|
|
|
|
linesTL[j].setLine(
|
|
c[corner].centerX - v.dx1( corner ),
|
|
c[corner].centerY - v.dy1( corner ),
|
|
c[corner].centerX - v.dx2( corner ),
|
|
c[corner].centerY - v.dy2( corner ),
|
|
borderMapTL.colorAt( j ) );
|
|
}
|
|
|
|
{
|
|
constexpr auto corner = TopRight;
|
|
|
|
linesTR[k].setLine(
|
|
c[corner].centerX + v.dx1( corner ),
|
|
c[corner].centerY - v.dy1( corner ),
|
|
c[corner].centerX + v.dx2( corner ),
|
|
c[corner].centerY - v.dy2( corner ),
|
|
borderMapTR.colorAt( k ) );
|
|
}
|
|
|
|
{
|
|
constexpr auto corner = BottomLeft;
|
|
|
|
linesBL[k].setLine(
|
|
c[corner].centerX - v.dx1( corner ),
|
|
c[corner].centerY + v.dy1( corner ),
|
|
c[corner].centerX - v.dx2( corner ),
|
|
c[corner].centerY + v.dy2( corner ),
|
|
borderMapBL.colorAt( k ) );
|
|
}
|
|
|
|
{
|
|
constexpr auto corner = BottomRight;
|
|
|
|
linesBR[j].setLine(
|
|
c[corner].centerX + v.dx1( corner ),
|
|
c[corner].centerY + v.dy1( corner ),
|
|
c[corner].centerX + v.dx2( corner ),
|
|
c[corner].centerY + v.dy2( corner ),
|
|
borderMapBR.colorAt( j ) );
|
|
}
|
|
}
|
|
|
|
if ( fillLines )
|
|
{
|
|
const auto& ri = m_metrics.innerQuad;
|
|
|
|
if ( orientation == Qt::Vertical )
|
|
{
|
|
const int j = it.step();
|
|
const int k = numFillLines - it.step() - 1;
|
|
|
|
const qreal x11 = c[TopLeft].centerX - v.dx1( TopLeft );
|
|
const qreal x12 = c[TopRight].centerX + v.dx1( TopRight );
|
|
const qreal y1 = c[TopLeft].centerY - v.dy1( TopLeft );
|
|
const auto c1 = fillMap.colorAt( ( y1 - ri.top ) / ri.height );
|
|
|
|
const qreal x21 = c[BottomLeft].centerX - v.dx1( BottomLeft );
|
|
const qreal x22 = c[BottomRight].centerX + v.dx1( BottomRight );
|
|
const qreal y2 = c[BottomLeft].centerY + v.dy1( BottomLeft );
|
|
const auto c2 = fillMap.colorAt( ( y2 - ri.top ) / ri.height );
|
|
|
|
fillLines[j].setLine( x11, y1, x12, y1, c1 );
|
|
fillLines[k].setLine( x21, y2, x22, y2, c2 );
|
|
}
|
|
else
|
|
{
|
|
const int j = stepCount - it.step();
|
|
const int k = numFillLines - 1 - stepCount + it.step();
|
|
|
|
const qreal x1 = c[TopLeft].centerX - v.dx1( TopLeft );
|
|
const qreal y11 = c[TopLeft].centerY - v.dy1( TopLeft );
|
|
const qreal y12 = c[BottomLeft].centerY + v.dy1( BottomLeft );;
|
|
const auto c1 = fillMap.colorAt( ( x1 - ri.left ) / ri.width );
|
|
|
|
const qreal x2 = c[TopRight].centerX + v.dx1( TopRight );
|
|
const qreal y21 = c[TopRight].centerY - v.dy1( TopRight );
|
|
const qreal y22 = c[BottomRight].centerY + v.dy1( BottomRight );;
|
|
const auto c2 = fillMap.colorAt( ( x2 - ri.left ) / ri.width );
|
|
|
|
fillLines[j].setLine( x1, y11, x1, y12, c1 );
|
|
fillLines[k].setLine( x2, y21, x2, y22, c2 );
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 1
|
|
if ( borderLines )
|
|
{
|
|
const int k = 4 * numCornerLines;
|
|
|
|
if ( orientation == Qt::Vertical )
|
|
borderLines[k] = borderLines[0];
|
|
else
|
|
borderLines[0] = borderLines[k];
|
|
}
|
|
#endif
|
|
}
|
|
|
|
private:
|
|
const QskBoxRenderer::Metrics& m_metrics;
|
|
};
|
|
}
|
|
|
|
static inline int qskFillLineCount(
|
|
const QskBoxRenderer::Metrics& metrics, const QskGradient& gradient )
|
|
{
|
|
const int stepCount = metrics.corner[0].stepCount;
|
|
|
|
if ( !gradient.isVisible() )
|
|
return 0;
|
|
|
|
int lineCount = 0;
|
|
|
|
switch ( gradient.orientation() )
|
|
{
|
|
case QskGradient::Diagonal:
|
|
{
|
|
lineCount += 2 * ( stepCount + 1 );
|
|
|
|
if ( metrics.centerQuad.left >= metrics.centerQuad.right )
|
|
lineCount--;
|
|
|
|
if ( metrics.centerQuad.top >= metrics.centerQuad.bottom )
|
|
lineCount--;
|
|
|
|
/*
|
|
For diagonal lines the points at the opposite
|
|
side are no points interpolating the outline.
|
|
So we need to insert interpolating lines on both sides
|
|
*/
|
|
|
|
lineCount *= 2; // a real ellipse could be done with lineCount lines: TODO ...
|
|
|
|
#if 1
|
|
/*
|
|
The termination of the fill algorithm is a bit random
|
|
and might result in having an additional line.
|
|
Until this effect is completely understood, we better
|
|
reserve memory for this to avoid crashes.
|
|
*/
|
|
|
|
lineCount++; // happens in a corner case - needs to be understood: TODO
|
|
#endif
|
|
|
|
break;
|
|
}
|
|
case QskGradient::Vertical:
|
|
{
|
|
lineCount += qMax( metrics.corner[ TopLeft ].stepCount,
|
|
metrics.corner[ TopRight ].stepCount ) + 1;
|
|
|
|
lineCount += qMax( metrics.corner[ BottomLeft ].stepCount,
|
|
metrics.corner[ BottomRight ].stepCount ) + 1;
|
|
|
|
if ( metrics.centerQuad.top >= metrics.centerQuad.bottom )
|
|
lineCount--;
|
|
|
|
break;
|
|
}
|
|
case QskGradient::Horizontal:
|
|
{
|
|
lineCount += qMax( metrics.corner[ TopLeft ].stepCount,
|
|
metrics.corner[ BottomLeft ].stepCount ) + 1;
|
|
|
|
lineCount += qMax( metrics.corner[ TopRight ].stepCount,
|
|
metrics.corner[ BottomRight ].stepCount ) + 1;
|
|
|
|
if ( metrics.centerQuad.left >= metrics.centerQuad.right )
|
|
lineCount--;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// adding vertexes for the stops - beside the first/last
|
|
|
|
if ( !gradient.isMonochrome() )
|
|
lineCount += gradient.stops().size() - 2;
|
|
|
|
return lineCount;
|
|
}
|
|
|
|
template< class Line, class BorderMap, class FillMap >
|
|
static inline void qskRenderLines( const QskBoxRenderer::Metrics& metrics,
|
|
Qt::Orientation orientation, Line* borderLines,
|
|
const BorderMap& borderMapTL, const BorderMap& borderMapTR,
|
|
const BorderMap& borderMapBL, const BorderMap& borderMapBR,
|
|
Line* fillLines, const FillMap& fillMap )
|
|
{
|
|
if ( metrics.isBorderRegular && metrics.isRadiusRegular )
|
|
{
|
|
// the same border width for all edges
|
|
|
|
Stroker< Line, BorderValuesUniform > stroker( metrics );
|
|
stroker.createLines( orientation, borderLines, borderMapTL, borderMapTR,
|
|
borderMapBL, borderMapBR, fillLines, fillMap );
|
|
}
|
|
else
|
|
{
|
|
Stroker< Line, BorderValues > stroker( metrics );
|
|
stroker.createLines( orientation, borderLines, borderMapTL, borderMapTR,
|
|
borderMapBL, borderMapBR, fillLines, fillMap );
|
|
}
|
|
}
|
|
|
|
template< class Line, class BorderMap, class FillMap >
|
|
static inline void qskRenderLines( const QskBoxRenderer::Metrics& metrics,
|
|
Qt::Orientation orientation, Line* borderLines,
|
|
const BorderMap& borderMap, Line* fillLines, const FillMap& fillMap )
|
|
{
|
|
qskRenderLines( metrics, orientation, borderLines, borderMap, borderMap,
|
|
borderMap, borderMap, fillLines, fillMap );
|
|
}
|
|
|
|
template< class Line, class BorderMap >
|
|
static inline void qskRenderBorderLines( const QskBoxRenderer::Metrics& metrics,
|
|
Qt::Orientation orientation, Line* lines,
|
|
const BorderMap& borderMapTL, const BorderMap& borderMapTR,
|
|
const BorderMap& borderMapBL, const BorderMap& borderMapBR )
|
|
{
|
|
qskRenderLines( metrics, orientation, lines, borderMapTL, borderMapTR,
|
|
borderMapBL, borderMapBR, static_cast< Line* >( nullptr ), ColorMapNone() );
|
|
}
|
|
|
|
template< class Line, class BorderMap >
|
|
static inline void qskRenderBorderLines( const QskBoxRenderer::Metrics& metrics,
|
|
Qt::Orientation orientation, Line* lines, const BorderMap& borderMap )
|
|
{
|
|
qskRenderBorderLines( metrics, orientation, lines,
|
|
borderMap, borderMap, borderMap, borderMap );
|
|
}
|
|
|
|
template< class Line, class FillMap >
|
|
static inline void qskRenderFillLines( const QskBoxRenderer::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,
|
|
Qt::Orientation orientation, const QskBoxBorderColors& colors, ColoredLine* line )
|
|
{
|
|
const auto& c = colors;
|
|
|
|
if ( colors.isMonochrome() )
|
|
{
|
|
qskRenderBorderLines( metrics, orientation, line, BorderMapSolid( c.rgb( Qsk::Left ) ) );
|
|
}
|
|
else
|
|
{
|
|
const int stepCount = metrics.corner[0].stepCount;
|
|
|
|
qskRenderBorderLines( metrics, orientation, line,
|
|
BorderMapGradient( stepCount, c.rgb( Qsk::Top ), c.rgb( Qsk::Left ) ),
|
|
BorderMapGradient( stepCount, c.rgb( Qsk::Right ), c.rgb( Qsk::Top ) ),
|
|
BorderMapGradient( stepCount, c.rgb( Qsk::Left ), c.rgb( Qsk::Bottom ) ),
|
|
BorderMapGradient( stepCount, c.rgb( Qsk::Bottom ), c.rgb( Qsk::Right ) )
|
|
);
|
|
}
|
|
}
|
|
|
|
static inline void qskRenderFillRandom( const QskBoxRenderer::Metrics& metrics,
|
|
const QskGradient& gradient, ColoredLine* line )
|
|
{
|
|
if ( gradient.isMonochrome() )
|
|
{
|
|
const ColorMapSolid map( gradient.startColor() );
|
|
qskRenderFillLines( metrics, Qt::Vertical, line, map );
|
|
}
|
|
else
|
|
{
|
|
const auto orientation = ( gradient.orientation() == QskGradient::Vertical )
|
|
? Qt::Vertical : Qt::Horizontal;
|
|
|
|
const ColorMapGradient map( gradient.startColor(), gradient.endColor() );
|
|
qskRenderFillLines( metrics, orientation, line, map );
|
|
}
|
|
}
|
|
|
|
static inline void qskRenderBoxRandom(
|
|
const QskBoxRenderer::Metrics& metrics, const QskBoxBorderColors& borderColors,
|
|
const QskGradient& gradient, ColoredLine* fillLine, ColoredLine* borderLine )
|
|
{
|
|
const auto& bc = borderColors;
|
|
|
|
if ( bc.isMonochrome() )
|
|
{
|
|
const BorderMapSolid borderMap( bc.rgb( Qsk::Left ) );
|
|
|
|
if ( gradient.isMonochrome() )
|
|
{
|
|
const ColorMapSolid fillMap( gradient.startColor() );
|
|
qskRenderLines( metrics, Qt::Vertical, borderLine, borderMap, fillLine, fillMap );
|
|
}
|
|
else
|
|
{
|
|
const auto orientation = ( gradient.orientation() == QskGradient::Vertical )
|
|
? Qt::Vertical : Qt::Horizontal;
|
|
|
|
const ColorMapGradient fillMap( gradient.startColor(), gradient.endColor() );
|
|
qskRenderLines( metrics, orientation, borderLine, borderMap, fillLine, fillMap );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const int n = metrics.corner[0].stepCount;
|
|
|
|
const BorderMapGradient tl( n, bc.rgb( Qsk::Top ), bc.rgb( Qsk::Left ) );
|
|
const BorderMapGradient tr( n, bc.rgb( Qsk::Right ), bc.rgb( Qsk::Top ) );
|
|
const BorderMapGradient bl( n, bc.rgb( Qsk::Left ), bc.rgb( Qsk::Bottom ) );
|
|
const BorderMapGradient br( n, bc.rgb( Qsk::Bottom ), bc.rgb( Qsk::Right ) );
|
|
|
|
if ( gradient.isMonochrome() )
|
|
{
|
|
const ColorMapSolid fillMap( gradient.startColor() );
|
|
qskRenderLines( metrics, Qt::Vertical, borderLine, tl, tr, bl, br, fillLine, fillMap );
|
|
}
|
|
else
|
|
{
|
|
const auto orientation = ( gradient.orientation() == QskGradient::Vertical )
|
|
? Qt::Vertical : Qt::Horizontal;
|
|
|
|
const ColorMapGradient fillMap( gradient.startColor(), gradient.endColor() );
|
|
qskRenderLines( metrics, orientation, borderLine, tl, tr, bl, br, fillLine, fillMap );
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void qskRenderFillOrdered( const QskBoxRenderer::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 ...
|
|
*/
|
|
|
|
if( gradient.orientation() == QskGradient::Horizontal )
|
|
{
|
|
HRectEllipseIterator it( metrics );
|
|
QskVertex::fillOrdered( it, r.left, r.right, gradient, lines );
|
|
}
|
|
else
|
|
{
|
|
VRectEllipseIterator it( metrics );
|
|
QskVertex::fillOrdered( it, r.top, r.bottom, gradient, lines );
|
|
}
|
|
}
|
|
|
|
QskBoxRenderer::Metrics::Metrics( const QRectF& rect,
|
|
const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& border ):
|
|
outerQuad( rect )
|
|
{
|
|
isRadiusRegular = shape.isRectellipse();
|
|
|
|
for ( int i = 0; i < 4; i++ )
|
|
{
|
|
auto& c = corner[i];
|
|
|
|
const QSizeF radius = shape.radius( static_cast< Qt::Corner >( i ) );
|
|
c.radiusX = qBound( 0.0, radius.width(), 0.5 * outerQuad.width );
|
|
c.radiusY = qBound( 0.0, radius.height(), 0.5 * outerQuad.height );
|
|
c.stepCount = ArcIterator::segmentHint( qMax( c.radiusX, c.radiusY ) );
|
|
|
|
switch( i )
|
|
{
|
|
case TopLeft:
|
|
c.centerX = outerQuad.left + c.radiusX;
|
|
c.centerY = outerQuad.top + c.radiusY;
|
|
break;
|
|
|
|
case TopRight:
|
|
c.centerX = outerQuad.right - c.radiusX;
|
|
c.centerY = outerQuad.top + c.radiusY;
|
|
break;
|
|
|
|
case BottomLeft:
|
|
c.centerX = outerQuad.left + c.radiusX;
|
|
c.centerY = outerQuad.bottom - c.radiusY;
|
|
break;
|
|
|
|
case BottomRight:
|
|
c.centerX = outerQuad.right - c.radiusX;
|
|
c.centerY = outerQuad.bottom - c.radiusY;
|
|
break;
|
|
}
|
|
}
|
|
|
|
centerQuad.left = qMax( corner[ TopLeft].centerX,
|
|
corner[ BottomLeft ].centerX );
|
|
|
|
centerQuad.right = qMin( corner[ TopRight].centerX,
|
|
corner[ BottomRight ].centerX );
|
|
|
|
centerQuad.top = qMax( corner[ TopLeft].centerY,
|
|
corner[ TopRight ].centerY );
|
|
|
|
centerQuad.bottom = qMin( corner[ BottomLeft].centerY,
|
|
corner[ BottomRight ].centerY );
|
|
|
|
centerQuad.width = centerQuad.right - centerQuad.left;
|
|
centerQuad.height = centerQuad.bottom - centerQuad.top;
|
|
|
|
// now the bounding rectangle of the fill area
|
|
|
|
const auto bw = border.widths();
|
|
|
|
innerQuad.left = outerQuad.left + bw.left();
|
|
innerQuad.right = outerQuad.right - bw.right();
|
|
innerQuad.top = outerQuad.top + bw.top();
|
|
innerQuad.bottom = outerQuad.bottom - bw.bottom();
|
|
|
|
innerQuad.left = qMin( innerQuad.left, centerQuad.right );
|
|
innerQuad.right = qMax( innerQuad.right, centerQuad.left );
|
|
innerQuad.top = qMin( innerQuad.top, centerQuad.bottom );
|
|
innerQuad.bottom = qMax( innerQuad.bottom, centerQuad.top );
|
|
|
|
if ( innerQuad.left > innerQuad.right )
|
|
{
|
|
innerQuad.left = innerQuad.right =
|
|
innerQuad.right + 0.5 * ( innerQuad.left - innerQuad.right );
|
|
}
|
|
|
|
if ( innerQuad.top > innerQuad.bottom )
|
|
{
|
|
innerQuad.top = innerQuad.bottom =
|
|
innerQuad.bottom + 0.5 * ( innerQuad.top - innerQuad.bottom );
|
|
}
|
|
|
|
innerQuad.width = innerQuad.right - innerQuad.left;
|
|
innerQuad.height = innerQuad.bottom - innerQuad.top;
|
|
|
|
const qreal borderLeft = innerQuad.left - outerQuad.left;
|
|
const qreal borderTop = innerQuad.top - outerQuad.top;
|
|
const qreal borderRight = outerQuad.right - innerQuad.right;
|
|
const qreal borderBottom = outerQuad.bottom - innerQuad.bottom;
|
|
|
|
for ( int i = 0; i < 4; i++ )
|
|
{
|
|
auto& c = corner[i];
|
|
|
|
switch( i )
|
|
{
|
|
case TopLeft:
|
|
{
|
|
c.radiusInnerX = c.radiusX - borderLeft;
|
|
c.radiusInnerY = c.radiusY - borderTop;
|
|
|
|
c.isCropped = ( c.centerX <= innerQuad.left )
|
|
|| ( c.centerY <= innerQuad.top );
|
|
|
|
break;
|
|
}
|
|
case TopRight:
|
|
{
|
|
c.radiusInnerX = c.radiusX - borderRight;
|
|
c.radiusInnerY = c.radiusY - borderTop;
|
|
|
|
c.isCropped = ( c.centerX >= innerQuad.right )
|
|
|| ( c.centerY <= innerQuad.top );
|
|
break;
|
|
}
|
|
case BottomLeft:
|
|
{
|
|
c.radiusInnerX = c.radiusX - borderLeft;
|
|
c.radiusInnerY = c.radiusY - borderBottom;
|
|
|
|
c.isCropped = ( c.centerX <= innerQuad.left )
|
|
|| ( c.centerY >= innerQuad.bottom );
|
|
break;
|
|
}
|
|
case BottomRight:
|
|
{
|
|
c.radiusInnerX = c.radiusX - borderRight;
|
|
c.radiusInnerY = c.radiusY - borderBottom;
|
|
|
|
c.isCropped = ( c.centerX >= innerQuad.right )
|
|
|| ( c.centerY >= innerQuad.bottom );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
isTotallyCropped =
|
|
corner[ TopLeft ].isCropped &&
|
|
corner[ TopRight ].isCropped &&
|
|
corner[ BottomRight ].isCropped &&
|
|
corner[ BottomLeft ].isCropped;
|
|
|
|
// number of steps for iterating over the corners
|
|
|
|
isBorderRegular = ( borderLeft == borderTop )
|
|
&& ( borderTop == borderRight ) && ( borderRight == borderBottom );
|
|
}
|
|
|
|
void QskBoxRenderer::renderRectellipseBorder(
|
|
const QRectF& rect, const QskBoxShapeMetrics& shape,
|
|
const QskBoxBorderMetrics& border, QSGGeometry& geometry )
|
|
{
|
|
const Metrics metrics( rect, shape, border );
|
|
|
|
if ( metrics.innerQuad == metrics.outerQuad )
|
|
{
|
|
allocateLines< Line >( geometry, 0 );
|
|
return;
|
|
}
|
|
|
|
const int stepCount = metrics.corner[0].stepCount;
|
|
const int lineCount = 4 * ( stepCount + 1 ) + 1;
|
|
|
|
const auto line = allocateLines< Line >( geometry, lineCount );
|
|
qskRenderBorderLines( metrics, Qt::Vertical, line, BorderMapNone() );
|
|
}
|
|
|
|
void QskBoxRenderer::renderRectellipseFill(
|
|
const QRectF& rect, const QskBoxShapeMetrics& shape,
|
|
const QskBoxBorderMetrics& border, QSGGeometry& geometry )
|
|
{
|
|
const Metrics metrics( rect, shape, border );
|
|
|
|
if ( ( metrics.innerQuad.width <= 0 ) || ( metrics.innerQuad.height <= 0 ) )
|
|
{
|
|
allocateLines< Line >( geometry, 0 );
|
|
return;
|
|
}
|
|
|
|
if ( metrics.isTotallyCropped )
|
|
{
|
|
// degenerated to a rectangle
|
|
|
|
const QRectF r( metrics.innerQuad.left, metrics.innerQuad.top,
|
|
metrics.innerQuad.width, metrics.innerQuad.height );
|
|
|
|
renderRectFill( r, QskBoxShapeMetrics(),
|
|
QskBoxBorderMetrics(), geometry );
|
|
|
|
return;
|
|
}
|
|
|
|
const int stepCount = metrics.corner[0].stepCount;
|
|
|
|
int lineCount = 2 * ( stepCount + 1 );
|
|
if ( metrics.centerQuad.top >= metrics.centerQuad.bottom )
|
|
lineCount++;
|
|
|
|
const auto line = allocateLines< Line >( geometry, lineCount );
|
|
qskRenderFillLines( metrics, Qt::Vertical, line, ColorMapNone() );
|
|
}
|
|
|
|
void QskBoxRenderer::renderRectellipse( const QRectF& rect,
|
|
const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& border,
|
|
const QskBoxBorderColors& borderColors, const QskGradient& gradient,
|
|
QSGGeometry& geometry )
|
|
{
|
|
const Metrics metrics( rect, shape, border );
|
|
|
|
int fillLineCount = 0;
|
|
|
|
if ( !metrics.innerQuad.isEmpty() && gradient.isVisible() )
|
|
{
|
|
if ( metrics.isTotallyCropped )
|
|
{
|
|
// degenerated to a rectangle
|
|
|
|
fillLineCount = gradient.stops().count();
|
|
|
|
#if 1
|
|
// code copied from QskBoxRendererRect.cpp TODO ...
|
|
|
|
if ( gradient.orientation() == QskGradient::Diagonal )
|
|
{
|
|
if ( metrics.centerQuad.width == metrics.centerQuad.height )
|
|
{
|
|
if ( !gradient.hasStopAt( 0.5 ) )
|
|
fillLineCount++;
|
|
}
|
|
else
|
|
{
|
|
// we might need extra lines for the corners
|
|
fillLineCount += 2;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
fillLineCount = qskFillLineCount( metrics, gradient );
|
|
}
|
|
}
|
|
|
|
const int stepCount = metrics.corner[0].stepCount;
|
|
|
|
int borderLineCount = 0;
|
|
if ( borderColors.isVisible() && metrics.innerQuad != metrics.outerQuad )
|
|
borderLineCount = 4 * ( stepCount + 1 ) + 1;
|
|
|
|
int lineCount = borderLineCount + fillLineCount;
|
|
|
|
bool extraLine = false;
|
|
if ( borderLineCount > 0 && fillLineCount > 0 )
|
|
{
|
|
if ( !gradient.isMonochrome() &&
|
|
( gradient.orientation() == QskGradient::Diagonal ) )
|
|
{
|
|
/*
|
|
The filling ends at 45° and we have no implementation
|
|
for creating the border from there. So we need to
|
|
insert an extra dummy line to connect fill and border
|
|
*/
|
|
extraLine = true;
|
|
lineCount++;
|
|
}
|
|
}
|
|
|
|
auto line = allocateLines< ColoredLine >( geometry, lineCount );
|
|
|
|
bool fillRandom = true;
|
|
if ( fillLineCount > 0 )
|
|
{
|
|
if ( metrics.isTotallyCropped )
|
|
{
|
|
fillRandom = false;
|
|
}
|
|
else if ( !gradient.isMonochrome() )
|
|
{
|
|
if ( gradient.stops().count() > 2 ||
|
|
gradient.orientation() == QskGradient::Diagonal )
|
|
{
|
|
fillRandom = false;
|
|
}
|
|
}
|
|
|
|
if ( fillRandom )
|
|
{
|
|
if ( !metrics.isRadiusRegular )
|
|
{
|
|
/*
|
|
When we have a combination of corners with the same
|
|
or no radius we could use the faster random algo: TODO ...
|
|
*/
|
|
fillRandom = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( ( fillLineCount > 0 ) && ( borderLineCount > 0 ) )
|
|
{
|
|
if ( fillRandom )
|
|
{
|
|
qskRenderBoxRandom( metrics, borderColors, gradient,
|
|
line, line + fillLineCount );
|
|
}
|
|
else
|
|
{
|
|
if ( metrics.isTotallyCropped )
|
|
{
|
|
renderRectFill( metrics.innerQuad, gradient, line );
|
|
}
|
|
else if ( gradient.orientation() == QskGradient::Diagonal )
|
|
{
|
|
renderDiagonalFill( metrics, gradient, fillLineCount, line );
|
|
}
|
|
else
|
|
{
|
|
qskRenderFillOrdered( metrics, gradient, line );
|
|
}
|
|
|
|
auto borderLines = line + fillLineCount;
|
|
if ( extraLine )
|
|
borderLines++;
|
|
|
|
const auto orientation = gradient.orientation() == QskGradient::Horizontal
|
|
? Qt::Horizontal : Qt::Vertical;
|
|
|
|
qskRenderBorder( metrics, orientation, borderColors, borderLines );
|
|
|
|
if ( extraLine )
|
|
{
|
|
const auto l = line + fillLineCount;
|
|
l[0].p1 = l[-1].p2;
|
|
l[0].p2 = l[1].p1;
|
|
}
|
|
}
|
|
}
|
|
else if ( fillLineCount > 0 )
|
|
{
|
|
if ( fillRandom )
|
|
{
|
|
qskRenderFillRandom( metrics, gradient, line );
|
|
}
|
|
else
|
|
{
|
|
if ( metrics.isTotallyCropped )
|
|
{
|
|
renderRectFill( metrics.innerQuad, gradient, line );
|
|
}
|
|
else if ( gradient.orientation() == QskGradient::Diagonal )
|
|
{
|
|
renderDiagonalFill( metrics, gradient, fillLineCount, line );
|
|
}
|
|
else
|
|
{
|
|
qskRenderFillOrdered( metrics, gradient, line );
|
|
}
|
|
}
|
|
}
|
|
else if ( borderLineCount > 0 )
|
|
{
|
|
#if 1
|
|
/*
|
|
In case of having an empty innerQuad and monochrome
|
|
border colors, we could treat it like filling without border. TODO ...
|
|
*/
|
|
#endif
|
|
qskRenderBorder( metrics, Qt::Vertical, borderColors, line );
|
|
}
|
|
}
|