qskinny/src/nodes/QskBoxBasicStroker.cpp

804 lines
25 KiB
C++
Raw Normal View History

/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
2023-04-06 09:23:37 +02:00
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "QskBoxBasicStroker.h"
#include "QskBoxColorMap.h"
namespace
{
inline int gradientLineCount( const QskGradient& borderGradient )
{
// only the intermediate gradient lines !
return qMax( 0, borderGradient.stepCount() - 1 );
}
inline void setGradientLineAt(
Qt::Orientation orientation, qreal x1, qreal y1, qreal x2, qreal y2,
const QskGradientStop& stop, QskVertex::ColoredLine* line )
{
if ( orientation == Qt::Horizontal )
{
const auto pos = x1 + stop.position() * ( x2 - x1 );
line->setLine( pos, y1, pos, y2, stop.rgb() );
}
else
{
const auto pos = y1 + stop.position() * ( y2 - y1 );
line->setLine( x1, pos, x2, pos, stop.rgb() );
}
}
inline int edgeToIndex( Qt::Edge edge )
2023-04-04 08:49:11 +02:00
{ return qCountTrailingZeroBits( static_cast< quint8 >( edge ) ); }
class CornerIterator : public QskVertex::ArcIterator
{
public:
inline CornerIterator( const QskBoxMetrics& metrics )
: m_corners( metrics.corners )
{
}
inline void resetSteps( int corner, bool inverted = false )
{
reset( m_corners[ corner ].stepCount, inverted );
}
inline void setBorderLine( int corner, QskVertex::Line* line ) const
{
const auto& c = m_corners[ corner ];
line->setLine( c.xInner( cos() ), c.yInner( sin() ),
c.xOuter( cos() ), c.yOuter( sin() ) );
}
protected:
const QskBoxMetrics::Corner* m_corners;
};
class CornerIteratorColor : public CornerIterator
{
public:
inline CornerIteratorColor( const QskBoxMetrics& metrics,
const QskBoxBorderColors& colors )
: CornerIterator( metrics )
, m_colors{
{ colors.top().rgbStart(), colors.left().rgbEnd() },
{ colors.top().rgbEnd(), colors.right().rgbStart() },
{ colors.bottom().rgbEnd(), colors.left().rgbStart() },
{ colors.bottom().rgbStart(), colors.right().rgbEnd() } }
{
}
inline void setBorderLine( int corner, QskVertex::ColoredLine* line ) const
{
const auto& c = m_corners[ corner ];
line->setLine( c.xInner( cos() ), c.yInner( sin() ),
c.xOuter( cos() ), c.yOuter( sin() ), color( corner ) );
}
private:
inline QskVertex::Color color( int corner ) const
{
const auto& cs = m_colors[ corner ];
if ( cs.first == cs.second )
return cs.first;
const auto ratio = step() / qreal( m_corners[ corner ].stepCount );
return cs.first.interpolatedTo( cs.second, ratio );
}
const QPair< QskVertex::Color, QskVertex::Color > m_colors[4];
};
class LineMap
{
public:
inline LineMap( const QskBoxMetrics& metrics )
: m_corners( metrics.corners )
{
}
inline void setHLine( int corner1, int corner2,
qreal cos, qreal sin, QskVertex::Line* line ) const
{
const qreal y = m_corners[ corner1 ].yInner( sin );
const qreal x1 = m_corners[ corner1 ].xInner( cos );
const qreal x2 = m_corners[ corner2 ].xInner( cos );
line->setLine( x1, y, x2, y );
}
inline void setVLine( int corner1, int corner2,
qreal cos, qreal sin, QskVertex::Line* line ) const
{
const qreal x = m_corners[ corner1 ].xInner( cos );
const qreal y1 = m_corners[ corner1 ].yInner( sin );
const qreal y2 = m_corners[ corner2 ].yInner( sin );
line->setLine( x, y1, x, y2 );
}
inline void setLine( int corner1, int corner2,
qreal cos, qreal sin, QskVertex::Line* line ) const
{
const qreal x1 = m_corners[ corner1 ].xInner( cos );
const qreal x2 = m_corners[ corner2 ].xInner( cos );
const qreal y1 = m_corners[ corner1 ].yInner( sin );
const qreal y2 = m_corners[ corner2 ].yInner( sin );
line->setLine( x1, y1, x2, y2 );
}
const QskBoxMetrics::Corner* m_corners;
};
class FillMap
{
public:
inline FillMap( const QskBoxMetrics& metrics, const QskBox::ColorMap& colorMap )
: m_colorMap( colorMap )
, m_corners( metrics.corners )
{
}
inline void setHLine( int corner1, int corner2,
qreal cos, qreal sin, QskVertex::ColoredLine* line ) const
{
const qreal y = m_corners[ corner1 ].yInner( sin );
const qreal x1 = m_corners[ corner1 ].xInner( cos );
const qreal x2 = m_corners[ corner2 ].xInner( cos );
m_colorMap.setLine( x1, y, x2, y, line );
}
inline void setVLine( int corner1, int corner2,
qreal cos, qreal sin, QskVertex::ColoredLine* line ) const
{
const qreal x = m_corners[ corner1 ].xInner( cos );
const qreal y1 = m_corners[ corner1 ].yInner( sin );
const qreal y2 = m_corners[ corner2 ].yInner( sin );
m_colorMap.setLine( x, y1, x, y2, line );
}
inline void setLine( int corner1, int corner2,
qreal cos, qreal sin, QskVertex::ColoredLine* line ) const
{
const qreal x1 = m_corners[ corner1 ].xInner( cos );
const qreal x2 = m_corners[ corner2 ].xInner( cos );
const qreal y1 = m_corners[ corner1 ].yInner( sin );
const qreal y2 = m_corners[ corner2 ].yInner( sin );
m_colorMap.setLine( x1, y1, x2, y2, line );
}
const QskBox::ColorMap& m_colorMap;
const QskBoxMetrics::Corner* m_corners;
};
}
static inline QskVertex::ColoredLine* qskAddGradientLines(
const QLineF& l1, const QLineF& l2, const QskGradient& gradient,
QskVertex::ColoredLine* lines )
{
const auto stops = gradient.stops();
if ( stops.first().position() > 0.0 )
( lines++ )->setLine( l1, stops.first().rgb() );
for( const auto& stop : stops )
{
const auto p1 = l1.p1() + stop.position() * ( l2.p1() - l1.p1() );
const auto p2 = l1.p2() + stop.position() * ( l2.p2() - l1.p2() );
( lines++ )->setLine( p1, p2, stop.rgb() );
}
if ( stops.last().position() < 1.0 )
( lines++ )->setLine( l2, stops.last().rgb() );
return lines;
}
inline void qskSetRectBorderLines( const QRectF& in, const QRectF& out,
const QskBoxBorderColors& colors, QskVertex::ColoredLine* lines )
{
const QLineF cl[4] =
{
{ in.right(), in.bottom(), out.right(), out.bottom() },
{ in.left(), in.bottom(), out.left(), out.bottom() },
{ in.left(), in.top(), out.left(), out.top() },
{ in.right(), in.top(), out.right(), out.top() }
};
if ( colors.isMonochrome() )
{
const QskVertex::Color c = colors.left().rgbStart();
lines[0].setLine( cl[0], c );
lines[1].setLine( cl[1], c );
lines[2].setLine( cl[2], c );
lines[3].setLine( cl[3], c );
lines[4] = lines[ 0 ];
}
else
{
lines = qskAddGradientLines( cl[0], cl[1], colors.bottom(), lines );
lines = qskAddGradientLines( cl[1], cl[2], colors.left(), lines );
lines = qskAddGradientLines( cl[2], cl[3], colors.top(), lines );
lines = qskAddGradientLines( cl[3], cl[0], colors.right(), lines );
}
}
template< class Line, class FillMap >
static inline void qskCreateFill(
const QskBoxMetrics& m_metrics, const FillMap& map, Line* lines )
{
using namespace QskVertex;
using namespace Qt;
const auto cn = m_metrics.corners;
const bool isHorizontal = m_metrics.preferredOrientation == Qt::Horizontal;
if ( !m_metrics.isInsideRounded )
{
map.setHLine( TopLeftCorner, TopRightCorner, 0.0, 1.0, lines );
map.setHLine( BottomLeftCorner, BottomRightCorner, 0.0, 1.0, lines + 1 );
}
else if ( m_metrics.isOutsideSymmetric )
{
const int stepCount = cn[ 0 ].stepCount;
if ( isHorizontal )
{
Line* l1 = lines + stepCount;
Line* l2 = lines + stepCount + 1;
for ( ArcIterator it( stepCount ); !it.isDone(); ++it )
{
map.setVLine( TopLeftCorner, BottomLeftCorner, it.cos(), it.sin(), l1++ );
map.setVLine( TopRightCorner, BottomRightCorner, it.cos(), it.sin(), l2-- );
}
}
else
{
Line* l1 = lines;
Line* l2 = lines + 2 * stepCount + 1;
for ( ArcIterator it( stepCount ); !it.isDone(); ++it )
{
map.setHLine( TopLeftCorner, TopRightCorner, it.cos(), it.sin(), l1++ );
map.setHLine( BottomLeftCorner, BottomRightCorner, it.cos(), it.sin(), l2-- );
}
}
}
else
{
auto line = lines;
int stepCount;
if ( isHorizontal )
{
stepCount = m_metrics.innerStepCount( TopLeftCorner, BottomLeftCorner );
for ( ArcIterator it( stepCount, true ); !it.isDone(); ++it )
map.setLine( TopLeftCorner, BottomLeftCorner, it.cos(), it.sin(), line++ );
stepCount = m_metrics.innerStepCount( TopRightCorner, BottomRightCorner );
for ( ArcIterator it( stepCount, false ); !it.isDone(); ++it )
map.setLine( TopRightCorner, BottomRightCorner, it.cos(), it.sin(), line++ );
}
else
{
stepCount = m_metrics.innerStepCount( TopLeftCorner, TopRightCorner );
for ( ArcIterator it( stepCount, false ); !it.isDone(); ++it )
map.setLine( TopLeftCorner, TopRightCorner, it.cos(), it.sin(), line++ );
stepCount = m_metrics.innerStepCount( BottomLeftCorner, BottomRightCorner );
for ( ArcIterator it( stepCount, true ); !it.isDone(); ++it )
map.setLine( BottomLeftCorner, BottomRightCorner, it.cos(), it.sin(), line++ );
}
}
}
QskBoxBasicStroker::GeometryLayout::GeometryLayout(
const QskBoxMetrics& metrics, const QskBoxBorderColors& colors )
{
const struct
{
Qt::Corner corner;
Qt::Edge edge;
} order[4] =
{
// counter clockwise
{ Qt::BottomRightCorner, Qt::RightEdge },
{ Qt::TopRightCorner, Qt::TopEdge },
{ Qt::TopLeftCorner, Qt::LeftEdge },
{ Qt::BottomLeftCorner, Qt::BottomEdge }
};
/*
In case of horizontal filling the lines end at right edge,
while for vertical filling it is the bottom edge.
*/
const int index0 = ( metrics.preferredOrientation == Qt::Horizontal ) ? 1 : 0;
int pos = index0;
for ( int i = 0; i < 4; i++ )
{
const int idx = ( index0 + i ) % 4;
const auto corner = order[ idx ].corner;
const auto edge = order[ idx ].edge;
this->cornerOffsets[ corner ] = pos;
pos += metrics.corners[ corner ].stepCount + 1;
this->edgeOffsets[ edgeToIndex( edge ) ] = pos;
pos += gradientLineCount( colors.gradientAt( edge ) );
}
if ( index0 == 0 )
{
this->closingOffsets[ 0 ] = 0;
this->closingOffsets[ 1 ] = pos;
this->lineCount = pos + 1;
}
else
{
pos--;
this->closingOffsets[ 0 ] = pos;
this->closingOffsets[ 1 ] = 0;
this->lineCount = pos + 1;
}
}
QskBoxBasicStroker::QskBoxBasicStroker( const QskBoxMetrics& metrics )
: m_metrics( metrics )
, m_geometryLayout( metrics, QskBoxBorderColors() )
, m_isColored( false )
{
}
QskBoxBasicStroker::QskBoxBasicStroker( const QskBoxMetrics& metrics,
const QskBoxBorderColors& borderColors )
: QskBoxBasicStroker( metrics, borderColors, QskBox::ColorMap() )
{
}
QskBoxBasicStroker::QskBoxBasicStroker( const QskBoxMetrics& metrics,
const QskBoxBorderColors& borderColors, const QskBox::ColorMap& colorMap )
: m_metrics( metrics )
, m_borderColors( borderColors )
, m_colorMap( colorMap )
, m_geometryLayout( metrics, m_borderColors )
, m_isColored( true )
{
}
void QskBoxBasicStroker::setBorderGradientLines(
QskVertex::ColoredLine* lines ) const
{
const auto off = m_geometryLayout.edgeOffsets;
setBorderGradientLines( Qt::TopEdge, lines + off[0] );
setBorderGradientLines( Qt::LeftEdge, lines + off[1] );
setBorderGradientLines( Qt::RightEdge, lines + off[2] );
setBorderGradientLines( Qt::BottomEdge, lines + off[3] );
}
void QskBoxBasicStroker::setBorderGradientLines(
Qt::Edge edge, QskVertex::ColoredLine* lines ) const
{
const auto& gradient = m_borderColors.gradientAt( edge );
if( gradient.stepCount() <= 1 )
{
// everything done as contour lines
return;
}
const auto cn = m_metrics.corners;
qreal x1, x2, y1, y2;
Qt::Orientation orientation;
switch( edge )
{
case Qt::LeftEdge:
{
orientation = Qt::Vertical;
x1 = m_metrics.innerRect.left();
x2 = m_metrics.outerRect.left();
y1 = cn[ Qt::BottomLeftCorner ].yInner( 0.0 );
y2 = cn[ Qt::TopLeftCorner ].yInner( 0.0 );
break;
}
case Qt::TopEdge:
{
orientation = Qt::Horizontal;
x1 = cn[ Qt::TopLeftCorner ].xInner( 0.0 );
x2 = cn[ Qt::TopRightCorner ].xInner( 0.0 );
y1 = m_metrics.innerRect.top();
y2 = m_metrics.outerRect.top();
break;
}
case Qt::BottomEdge:
{
orientation = Qt::Horizontal;
x1 = cn[ Qt::BottomRightCorner ].xInner( 0.0 );
x2 = cn[ Qt::BottomLeftCorner ].xInner( 0.0 );
y1 = m_metrics.innerRect.bottom();
y2 = m_metrics.outerRect.bottom();
break;
}
case Qt::RightEdge:
2023-04-06 10:06:00 +02:00
default:
{
orientation = Qt::Vertical;
x1 = m_metrics.innerRect.right();
x2 = m_metrics.outerRect.right();
y1 = cn[ Qt::TopRightCorner ].yInner( 0.0 );
y2 = cn[ Qt::BottomRightCorner ].yInner( 0.0 );
break;
}
}
auto line = lines;
const auto& stops = gradient.stops();
if ( stops.last().position() < 1.0 )
setGradientLineAt( orientation, x1, y1, x2, y2, stops.last(), line++ );
for( int i = stops.count() - 2; i >= 1; i-- )
setGradientLineAt( orientation, x1, y1, x2, y2, stops[i], line++ );
if ( stops.first().position() > 0.0 )
setGradientLineAt( orientation, x1, y1, x2, y2, stops.first(), line++ );
}
void QskBoxBasicStroker::setBorderLines( QskVertex::Line* lines ) const
{
Q_ASSERT( !m_isColored );
if ( !m_metrics.isOutsideRounded )
{
const auto& out = m_metrics.outerRect;
const auto& in = m_metrics.innerRect;
lines[0].setLine( in.right(), in.bottom(), out.right(), out.bottom() );
lines[1].setLine( in.left(), in.bottom(), out.left(), out.bottom() );
lines[2].setLine( in.left(), in.top(), out.left(), out.top() );
lines[3].setLine( in.right(), in.top(), out.right(), out.top() );
lines[4] = lines[ 0 ];
return;
}
const auto& gl = m_geometryLayout;
const auto cn = m_metrics.corners;
auto linesTL = lines + gl.cornerOffsets[ Qt::TopLeftCorner ];
auto linesTR = lines + gl.cornerOffsets[ Qt::TopRightCorner ]
+ cn[ Qt::TopRightCorner ].stepCount;
auto linesBL = lines + gl.cornerOffsets[ Qt::BottomLeftCorner ]
+ cn[ Qt::BottomLeftCorner ].stepCount;
auto linesBR = lines + gl.cornerOffsets[ Qt::BottomRightCorner ];
CornerIterator it( m_metrics );
if ( m_metrics.isOutsideSymmetric && m_metrics.isInsideRounded )
{
for ( it.resetSteps( Qt::TopLeftCorner ); !it.isDone(); ++it )
{
it.setBorderLine( Qt::TopLeftCorner, linesTL++ );
it.setBorderLine( Qt::TopRightCorner, linesTR-- );
it.setBorderLine( Qt::BottomLeftCorner, linesBL-- );
it.setBorderLine( Qt::BottomRightCorner, linesBR++ );
}
}
else
{
for ( it.resetSteps( Qt::TopLeftCorner ); !it.isDone(); ++it )
it.setBorderLine( Qt::TopLeftCorner, linesTL++ );
for ( it.resetSteps( Qt::TopRightCorner ); !it.isDone(); ++it )
it.setBorderLine( Qt::TopRightCorner, linesTR-- );
for ( it.resetSteps( Qt::BottomLeftCorner ); !it.isDone(); ++it )
it.setBorderLine( Qt::BottomLeftCorner, linesBL-- );
for ( it.resetSteps( Qt::BottomRightCorner ); !it.isDone(); ++it )
it.setBorderLine( Qt::BottomRightCorner, linesBR++);
}
lines[ gl.closingOffsets[ 1 ] ] = lines[ gl.closingOffsets[ 0 ] ];
}
void QskBoxBasicStroker::setBorderLines( QskVertex::ColoredLine* lines ) const
{
Q_ASSERT( m_isColored );
Q_ASSERT( lines );
if ( !m_metrics.isOutsideRounded )
{
qskSetRectBorderLines( m_metrics.innerRect,
m_metrics.outerRect, m_borderColors, lines );
return;
}
const auto& gl = m_geometryLayout;
const auto cn = m_metrics.corners;
auto linesTL = lines + gl.cornerOffsets[ Qt::TopLeftCorner ];
auto linesTR = lines + gl.cornerOffsets[ Qt::TopRightCorner ]
+ cn[ Qt::TopRightCorner ].stepCount;
auto linesBL = lines + gl.cornerOffsets[ Qt::BottomLeftCorner ]
+ cn[ Qt::BottomLeftCorner ].stepCount;
auto linesBR = lines + gl.cornerOffsets[ Qt::BottomRightCorner ];
CornerIteratorColor it( m_metrics, m_borderColors );
if ( m_metrics.isOutsideSymmetric && m_metrics.isInsideRounded )
{
for ( it.resetSteps( Qt::TopLeftCorner ); !it.isDone(); ++it )
{
it.setBorderLine( Qt::TopLeftCorner, linesTL++ );
it.setBorderLine( Qt::TopRightCorner, linesTR-- );
it.setBorderLine( Qt::BottomLeftCorner, linesBL-- );
it.setBorderLine( Qt::BottomRightCorner, linesBR++ );
}
}
else
{
for ( it.resetSteps( Qt::TopLeftCorner ); !it.isDone(); ++it )
it.setBorderLine( Qt::TopLeftCorner, linesTL++ );
for ( it.resetSteps( Qt::TopRightCorner ); !it.isDone(); ++it )
it.setBorderLine( Qt::TopRightCorner, linesTR-- );
for ( it.resetSteps( Qt::BottomLeftCorner ); !it.isDone(); ++it )
it.setBorderLine( Qt::BottomLeftCorner, linesBL-- );
for ( it.resetSteps( Qt::BottomRightCorner ); !it.isDone(); ++it )
it.setBorderLine( Qt::BottomRightCorner, linesBR++ );
}
setBorderGradientLines( lines );
lines[ gl.closingOffsets[ 1 ] ] = lines[ gl.closingOffsets[ 0 ] ];
}
void QskBoxBasicStroker::setFillLines( QskVertex::Line* lines ) const
{
Q_ASSERT( !m_isColored );
Q_ASSERT( lines );
if ( m_metrics.isInsideRounded )
{
const LineMap map( m_metrics );
qskCreateFill( m_metrics, map, lines );
}
else // a rectangle
{
const auto& in = m_metrics.innerRect;
lines[0].setLine( in.left(), in.top(), in.right(), in.top() );
lines[1].setLine( in.left(), in.bottom(), in.right(), in.bottom() );
}
}
void QskBoxBasicStroker::setFillLines( QskVertex::ColoredLine* lines ) const
{
Q_ASSERT( m_isColored );
Q_ASSERT( lines );
Q_ASSERT( !m_colorMap.isTransparent() );
if ( m_metrics.isInsideRounded )
{
const FillMap map( m_metrics, m_colorMap );
qskCreateFill( m_metrics, map, lines );
}
else // a rectangle
{
const auto& in = m_metrics.innerRect;
m_colorMap.setLine( in.left(), in.top(), in.right(), in.top(), lines + 0 );
m_colorMap.setLine( in.left(), in.bottom(), in.right(), in.bottom(), lines + 1 );
}
}
void QskBoxBasicStroker::setBoxLines( QskVertex::ColoredLine* borderLines,
QskVertex::ColoredLine* fillLines ) const
{
Q_ASSERT( m_isColored );
Q_ASSERT( borderLines || fillLines );
Q_ASSERT( fillLines == nullptr || !m_colorMap.isTransparent() );
if ( m_metrics.isOutsideSymmetric && m_metrics.isInsideRounded )
{
if ( borderLines && fillLines )
{
/*
Doing all in one allows a slightly faster implementation.
As this is the by far most common situation we do this
micro optimization.
*/
setBorderAndFillLines( borderLines, fillLines );
return;
}
}
if ( borderLines )
setBorderLines( borderLines );
if ( fillLines )
setFillLines( fillLines );
}
void QskBoxBasicStroker::setBorderAndFillLines(
QskVertex::ColoredLine* borderLines, QskVertex::ColoredLine* fillLines ) const
{
using namespace Qt;
const auto& gl = m_geometryLayout;
const FillMap fillMap( m_metrics, m_colorMap );
CornerIteratorColor it( m_metrics, m_borderColors );
/*
It would be possible to run over [0, 0.5 * M_PI_2]
and create 8 values ( instead of 4 ) in each step. TODO ...
*/
const auto stepCount = m_metrics.corners[0].stepCount;
auto linesTL = borderLines + gl.cornerOffsets[ TopLeftCorner ];
auto linesTR = borderLines + gl.cornerOffsets[ TopRightCorner ] + stepCount;
auto linesBL = borderLines + gl.cornerOffsets[ BottomLeftCorner ] + stepCount;
auto linesBR = borderLines + gl.cornerOffsets[ BottomRightCorner ];
if ( m_metrics.preferredOrientation == Qt::Horizontal )
{
auto l1 = fillLines + stepCount;
auto l2 = fillLines + stepCount + 1;
for ( it.resetSteps( TopLeftCorner ); !it.isDone(); ++it )
{
it.setBorderLine( TopLeftCorner, linesTL++ );
it.setBorderLine( TopRightCorner, linesTR-- );
it.setBorderLine( BottomLeftCorner, linesBL-- );
it.setBorderLine( BottomRightCorner, linesBR++ );
fillMap.setVLine( TopLeftCorner, BottomLeftCorner, it.cos(), it.sin(), l1-- );
fillMap.setVLine( TopRightCorner, BottomRightCorner, it.cos(), it.sin(), l2++ );
}
}
else
{
auto l1 = fillLines;
auto l2 = fillLines + 2 * stepCount + 1;
for ( it.resetSteps( TopLeftCorner ); !it.isDone(); ++it )
{
it.setBorderLine( TopLeftCorner, linesTL++ );
it.setBorderLine( TopRightCorner, linesTR-- );
it.setBorderLine( BottomLeftCorner, linesBL-- );
it.setBorderLine( BottomRightCorner, linesBR++ );
fillMap.setHLine( TopLeftCorner, TopRightCorner, it.cos(), it.sin(), l1++ );
fillMap.setHLine( BottomLeftCorner, BottomRightCorner, it.cos(), it.sin(), l2-- );
}
}
if ( borderLines )
{
setBorderGradientLines( borderLines );
borderLines[ gl.closingOffsets[ 1 ] ] = borderLines[ gl.closingOffsets[ 0 ] ];
}
}
int QskBoxBasicStroker::borderCount() const
{
if ( !m_metrics.hasBorder )
return 0;
if ( m_isColored && !m_borderColors.isVisible() )
return 0;
int n = 0;
if ( m_metrics.isOutsideRounded )
{
/*
4: Number of lines is always one more than the number of steps.
1: extra line at the end to close the border path
*/
n = m_metrics.outerStepCount() + 4 + 1;
if ( m_isColored && !m_borderColors.isMonochrome() )
{
n += gradientLineCount( m_borderColors.left() );
n += gradientLineCount( m_borderColors.top() );
n += gradientLineCount( m_borderColors.right() );
n += gradientLineCount( m_borderColors.bottom() );
}
}
else
{
/*
4: One for each corner
1: extra line at the end to close the border path
*/
n = 4 + 1;
if ( m_isColored && !m_borderColors.isMonochrome() )
{
const int gradientLines = -1
+ m_borderColors.left().stepCount()
+ m_borderColors.top().stepCount()
+ m_borderColors.right().stepCount()
+ m_borderColors.bottom().stepCount();
n += qMax( gradientLines, 0 );
}
}
return n;
}
int QskBoxBasicStroker::fillCount() const
{
if ( m_metrics.innerRect.isEmpty() )
return 0;
if ( m_isColored && m_colorMap.isTransparent() )
return 0;
int n = 2;
if ( m_metrics.isInsideRounded )
{
if ( m_metrics.preferredOrientation == Qt::Horizontal )
{
n += m_metrics.innerStepCount( Qt::TopLeftCorner, Qt::BottomLeftCorner );
n += m_metrics.innerStepCount( Qt::TopRightCorner, Qt::BottomRightCorner );
}
else
{
n += m_metrics.innerStepCount( Qt::TopLeftCorner, Qt::TopRightCorner );
n += m_metrics.innerStepCount( Qt::BottomLeftCorner, Qt::BottomRightCorner );
}
}
return n;
}