qskinny/src/nodes/QskRoundedRect.cpp

897 lines
28 KiB
C++

/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#include "QskRoundedRect.h"
#include "QskGradient.h"
#include "QskGradientDirection.h"
#include "QskBoxShapeMetrics.h"
#include "QskBoxBorderColors.h"
#include "QskBoxBorderMetrics.h"
namespace
{
Qt::Orientation orientationOf( const QskGradient& gradient )
{
if ( gradient.isMonochrome() )
return Qt::Vertical;
return gradient.linearDirection().isVertical()
? Qt::Vertical : Qt::Horizontal;
}
inline int extraStops( const QskGradient& gradient )
{
return qMax( 0, gradient.stepCount() - 1 );
}
inline void setBorderGradientLine(
const QskVertex::Line& l, float dx1, float dy1, float dx2, float dy2,
const QskGradientStop& stop, QskVertex::ColoredLine* lines )
{
const auto pos = stop.position();
const float x1 = l.p1.x + pos * dx1;
const float y1 = l.p1.y + pos * dy1;
const float x2 = l.p2.x + pos * dx2;
const float y2 = l.p2.y + pos * dy2;
lines->setLine( x1, y1, x2, y2, stop.rgb() );
}
class ColorMap
{
public:
inline ColorMap( const QskGradient& gradient )
: m_isMonochrome( gradient.isMonochrome() )
, m_color1( gradient.rgbStart() )
, m_color2( gradient.rgbEnd() )
{
}
inline QskVertex::Color colorAt( qreal value ) const
{
if ( m_isMonochrome )
return m_color1;
else
return m_color1.interpolatedTo( m_color2, value );
}
private:
const bool m_isMonochrome;
const QskVertex::Color m_color1;
const QskVertex::Color m_color2;
};
class BorderMap
{
public:
inline BorderMap( int stepCount, const QskGradient& g1, const QskGradient& g2 )
: m_stepCount( stepCount )
, m_color1( g1.rgbStart() )
, m_color2( g2.rgbEnd() )
{
}
inline QskVertex::Color colorAt( int step ) const
{
if ( m_color1 == m_color2 )
return m_color1;
else
return m_color1.interpolatedTo( m_color2, step / m_stepCount );
}
private:
const qreal m_stepCount;
const QskVertex::Color m_color1, m_color2;
};
class BorderMaps
{
public:
inline BorderMaps( int stepCount, const QskBoxBorderColors& colors )
: maps{
{ stepCount, colors.top(), colors.left() },
{ stepCount, colors.right(), colors.top() },
{ stepCount, colors.left(), colors.bottom() },
{ stepCount, colors.bottom(), colors.right() } }
{
}
const BorderMap maps[4];
};
}
int QskRoundedRect::extraBorderStops( const QskBoxBorderColors& bc )
{
return extraStops( bc.left() ) + extraStops( bc.top() )
+ extraStops( bc.right() ) + extraStops( bc.bottom() );
}
QskRoundedRect::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 = corners[ 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( corners[ TopLeft ].centerX,
corners[ BottomLeft ].centerX );
centerQuad.right = qMin( corners[ TopRight ].centerX,
corners[ BottomRight ].centerX );
centerQuad.top = qMax( corners[ TopLeft ].centerY,
corners[ TopRight ].centerY );
centerQuad.bottom = qMin( corners[ BottomLeft ].centerY,
corners[ 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 = corners[ 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 =
corners[ TopLeft ].isCropped &&
corners[ TopRight ].isCropped &&
corners[ BottomRight ].isCropped &&
corners[ BottomLeft ].isCropped;
// number of steps for iterating over the corners
isBorderRegular =
( borderLeft == borderTop ) &&
( borderTop == borderRight ) &&
( borderRight == borderBottom );
}
QskRoundedRect::BorderValues::BorderValues( const QskRoundedRect::Metrics& metrics )
: m_metrics( metrics )
, m_isUniform( metrics.isBorderRegular && metrics.isRadiusRegular )
{
if ( m_isUniform )
{
const auto& c = metrics.corners[ 0 ];
m_uniform.dx1 = c.radiusInnerX;
m_uniform.dy1 = c.radiusInnerY;
}
else
{
for ( int i = 0; i < 4; i++ )
{
const auto& c = metrics.corners[ i ];
auto& inner = m_multi.inner[ i ];
auto& outer = m_multi.outer[ i ];
if ( c.radiusInnerX >= 0.0 )
{
inner.x0 = 0.0;
inner.rx = c.radiusInnerX;
}
else
{
// should also be c.isCropped !
inner.x0 = c.radiusInnerX;
inner.rx = 0.0;
}
if ( c.radiusInnerY >= 0.0 )
{
inner.y0 = 0.0;
inner.ry = c.radiusInnerY;
}
else
{
// should also be c.isCropped !
inner.y0 = c.radiusInnerY;
inner.ry = 0.0;
}
outer.x0 = outer.y0 = 0.0;
outer.rx = c.radiusX;
outer.ry = c.radiusY;
}
}
}
void QskRoundedRect::BorderValues::setAngle( qreal cos, qreal sin )
{
if ( m_isUniform )
{
const auto& c = m_metrics.corners[ 0 ];
if ( !c.isCropped )
{
m_uniform.dx1 = cos * c.radiusInnerX;
m_uniform.dy1 = sin * c.radiusInnerY;
}
m_uniform.dx2 = cos * c.radiusX;
m_uniform.dy2 = sin * c.radiusY;
}
else
{
auto inner = m_multi.inner;
auto outer = m_multi.outer;
inner[ 0 ].setAngle( cos, sin );
inner[ 1 ].setAngle( cos, sin );
inner[ 2 ].setAngle( cos, sin );
inner[ 3 ].setAngle( cos, sin );
outer[ 0 ].setAngle( cos, sin );
if ( !m_metrics.isRadiusRegular )
{
outer[ 1 ].setAngle( cos, sin );
outer[ 2 ].setAngle( cos, sin );
outer[ 3 ].setAngle( cos, sin );
}
}
}
void QskRoundedRect::Stroker::setBorderGradientLines(
const BorderValues& v, int c1, const QskGradient& gradient,
QskVertex::ColoredLine* lines ) const
{
if( gradient.stepCount() <= 1 )
{
// everything done as contour lines
return;
}
QskVertex::Line from, to;
const auto& c = m_metrics.corners;
switch( c1 )
{
case TopLeft:
{
const auto c2 = BottomLeft;
to.p1.x = c[ c1 ].centerX - v.dx1( c1 );
to.p1.y = c[ c1 ].centerY - v.dy1( c1 );
to.p2.x = c[ c1 ].centerX - v.dx2( c1 );
to.p2.y = c[ c1 ].centerY - v.dy2( c1 );
from.p1.x = c[ c2 ].centerX - v.dx1( c2 );
from.p1.y = c[ c2 ].centerY + v.dy1( c2 );
from.p2.x = c[ c2 ].centerX - v.dx2( c2 );
from.p2.y = c[ c2 ].centerY + v.dy2( c2 );
break;
}
case TopRight:
{
const auto c2 = TopLeft;
to.p1.x = c[ c1 ].centerX + v.dx1( c1 );
to.p1.y = c[ c1 ].centerY - v.dy1( c1 );
to.p2.x = c[ c1 ].centerX + v.dx2( c1 );
to.p2.y = c[ c1 ].centerY - v.dy2( c1 );
from.p1.x = c[ c2 ].centerX - v.dx1( c2 );
from.p1.y = c[ c2 ].centerY - v.dy1( c2 );
from.p2.x = c[ c2 ].centerX - v.dx2( c2 );
from.p2.y = c[ c2 ].centerY - v.dy2( c2 );
break;
}
case BottomLeft:
{
const auto c2 = BottomRight;
to.p1.x = c[ c1 ].centerX - v.dx1( c1 );
to.p1.y = c[ c1 ].centerY + v.dy1( c1 );
to.p2.x = c[ c1 ].centerX - v.dx2( c1 );
to.p2.y = c[ c1 ].centerY + v.dy2( c1 );
from.p1.x = c[ c2 ].centerX + v.dx1( c2 );
from.p1.y = c[ c2 ].centerY + v.dy1( c2 );
from.p2.x = c[ c2 ].centerX + v.dx2( c2 );
from.p2.y = c[ c2 ].centerY + v.dy2( c2 );
break;
}
case BottomRight:
{
const auto c2 = TopRight;
to.p1.x = c[ c1 ].centerX + v.dx1( c1 );
to.p1.y = c[ c1 ].centerY + v.dy1( c1 );
to.p2.x = c[ c1 ].centerX + v.dx2( c1 );
to.p2.y = c[ c1 ].centerY + v.dy2( c1 );
from.p1.x = c[ c2 ].centerX + v.dx1( c2 );
from.p1.y = c[ c2 ].centerY - v.dy1( c2 );
from.p2.x = c[ c2 ].centerX + v.dx2( c2 );
from.p2.y = c[ c2 ].centerY - v.dy2( c2 );
break;
}
}
const float dx1 = to.p1.x - from.p1.x;
const float dy1 = to.p1.y - from.p1.y;
const float dx2 = to.p2.x - from.p2.x;
const float dy2 = to.p2.y - from.p2.y;
const auto stops = gradient.stops();
auto line = lines;
{
const auto& stop = stops.last();
if ( stop.position() < 1.0 )
setBorderGradientLine( from, dx1, dy1, dx2, dy2, stop, line++ );
}
for( int i = stops.count() - 2; i >= 1; i-- )
setBorderGradientLine( from, dx1, dy1, dx2, dy2, stops[i], line++ );
{
const auto& stop = stops.first();
if ( stop.position() > 0.0 )
setBorderGradientLine( from, dx1, dy1, dx2, dy2, stop, line++ );
}
}
void QskRoundedRect::Stroker::createBorderLines( QskVertex::Line* lines ) const
{
Q_ASSERT( m_metrics.isRadiusRegular );
const auto& c = m_metrics.corners;
const int stepCount = c[ 0 ].stepCount;
const int numCornerLines = stepCount + 1;
QskVertex::Line* linesBR = lines;
QskVertex::Line* linesTR = linesBR + numCornerLines;
QskVertex::Line* linesTL = linesTR + numCornerLines;
QskVertex::Line* linesBL = linesTL + numCornerLines;
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() );
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 ) );
}
{
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 ) );
}
{
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 ) );
}
{
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 ) );
}
}
lines[ 4 * numCornerLines ] = lines[ 0 ];
}
void QskRoundedRect::Stroker::createFillLines(
QskVertex::ColoredLine* lines, const QskGradient& gradient ) const
{
Q_ASSERT( m_metrics.isRadiusRegular );
Q_ASSERT( gradient.isValid() && gradient.stepCount() <= 1 );
ColorMap fillMap( gradient );
const auto& c = m_metrics.corners;
const int stepCount = c[ 0 ].stepCount;
int numLines = 2 * stepCount + 1;
const auto orientation = orientationOf( gradient );
if ( orientation == Qt::Vertical )
{
if ( m_metrics.centerQuad.top < m_metrics.centerQuad.bottom )
numLines++;
}
else
{
if ( m_metrics.centerQuad.left < m_metrics.centerQuad.right )
numLines++;
}
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() );
const auto& ri = m_metrics.innerQuad;
if ( orientation == Qt::Vertical )
{
const int j = it.step();
const int k = numLines - 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 );
lines[ j ].setLine( x11, y1, x12, y1, c1 );
lines[ k ].setLine( x21, y2, x22, y2, c2 );
}
else
{
const int j = stepCount - it.step();
const int k = numLines - 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 );
lines[ j ].setLine( x1, y11, x1, y12, c1 );
lines[ k ].setLine( x2, y21, x2, y22, c2 );
}
}
}
void QskRoundedRect::Stroker::createBorderLines(
Qt::Orientation orientation, QskVertex::ColoredLine* lines,
const QskBoxBorderColors& colors ) const
{
const auto& c = m_metrics.corners;
#if 1
const int stepCount = c[ 0 ].stepCount;
#endif
const BorderMaps borderMaps( stepCount, colors );
QskVertex::ColoredLine* linesBR, * linesTR, * linesTL, * linesBL;
const int numCornerLines = stepCount + 1;
if ( orientation == Qt::Vertical )
{
linesBR = lines;
linesTR = linesBR + numCornerLines + extraStops( colors.right() );
linesTL = linesTR + numCornerLines + extraStops( colors.top() );
linesBL = linesTL + numCornerLines + extraStops( colors.left() );
}
else
{
linesTR = lines + 1;
linesTL = linesTR + numCornerLines + extraStops( colors.top() );
linesBL = linesTL + numCornerLines + extraStops( colors.left() );
linesBR = linesBL + numCornerLines + extraStops( colors.bottom() );
}
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() );
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 ),
borderMaps.maps[ corner ].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 ),
borderMaps.maps[ corner ].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 ),
borderMaps.maps[ corner ].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 ),
borderMaps.maps[ corner ].colorAt( j ) );
}
}
v.setAngle( 0.0, 1.0 );
setBorderGradientLines( v, TopRight,
colors.top(), linesTR + numCornerLines );
setBorderGradientLines( v, BottomLeft,
colors.bottom(), linesBL + numCornerLines );
v.setAngle( 1.0, 0.0 );
setBorderGradientLines( v, TopLeft,
colors.left(), linesTL + numCornerLines );
setBorderGradientLines( v, BottomRight,
colors.right(), linesBR + numCornerLines );
const int k = 4 * numCornerLines + extraBorderStops( colors );
if ( orientation == Qt::Vertical )
lines[ k ] = lines[ 0 ];
else
lines[ 0 ] = lines[ k ];
}
void QskRoundedRect::Stroker::createUniformBox(
QskVertex::ColoredLine* borderLines, const QskBoxBorderColors& borderColors,
QskVertex::ColoredLine* fillLines, const QskGradient& gradient ) const
{
Q_ASSERT( m_metrics.isRadiusRegular );
Q_ASSERT( gradient.isValid() && gradient.stepCount() <= 1 );
const ColorMap fillMap( gradient );
const auto& c = m_metrics.corners;
const int stepCount = c[ 0 ].stepCount;
const BorderMaps borderMaps( stepCount, borderColors );
QskVertex::ColoredLine* linesBR, * linesTR, * linesTL, * linesBL;
linesBR = linesTR = linesTL = linesBL = nullptr;
const int numCornerLines = stepCount + 1;
int numFillLines = fillLines ? 2 * numCornerLines : 0;
const auto orientation = orientationOf( gradient );
if ( orientation == Qt::Vertical )
{
if ( borderLines )
{
linesBR = borderLines;
linesTR = linesBR + numCornerLines + extraStops( borderColors.right() );
linesTL = linesTR + numCornerLines + extraStops( borderColors.top() );
linesBL = linesTL + numCornerLines + extraStops( borderColors.left() );
}
if ( fillLines )
{
if ( m_metrics.centerQuad.top >= m_metrics.centerQuad.bottom )
numFillLines--;
}
}
else
{
if ( borderLines )
{
linesTR = borderLines + 1;
linesTL = linesTR + numCornerLines + extraStops( borderColors.top() );
linesBL = linesTL + numCornerLines + extraStops( borderColors.left() );
linesBR = linesBL + numCornerLines + extraStops( borderColors.bottom() );
}
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 ),
borderMaps.maps[ corner ].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 ),
borderMaps.maps[ corner ].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 ),
borderMaps.maps[ corner ].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 ),
borderMaps.maps[ corner ].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 ( borderLines )
{
v.setAngle( 0.0, 1.0 );
setBorderGradientLines( v, TopRight,
borderColors.top(), linesTR + numCornerLines );
setBorderGradientLines( v, BottomLeft,
borderColors.bottom(), linesBL + numCornerLines );
v.setAngle( 1.0, 0.0 );
setBorderGradientLines( v, TopLeft,
borderColors.left(), linesTL + numCornerLines );
setBorderGradientLines( v, BottomRight,
borderColors.right(), linesBR + numCornerLines );
const int k = 4 * numCornerLines + extraBorderStops( borderColors );
if ( orientation == Qt::Vertical )
borderLines[ k ] = borderLines[ 0 ];
else
borderLines[ 0 ] = borderLines[ k ];
}
}