2017-10-17 17:29:02 +02:00
|
|
|
/******************************************************************************
|
|
|
|
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
|
|
|
* This file may be used under the terms of the QSkinny License, Version 1.0
|
|
|
|
*****************************************************************************/
|
|
|
|
|
2022-12-28 19:39:08 +01:00
|
|
|
#include "QskRoundedRectRenderer.h"
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
#include "QskBoxBorderColors.h"
|
2018-08-03 08:15:28 +02:00
|
|
|
#include "QskBoxBorderMetrics.h"
|
|
|
|
#include "QskBoxRendererColorMap.h"
|
2017-10-17 17:29:02 +02:00
|
|
|
#include "QskBoxShapeMetrics.h"
|
2022-11-13 17:22:09 +01:00
|
|
|
#include "QskGradientDirection.h"
|
2022-12-28 19:39:08 +01:00
|
|
|
#include "QskRectRenderer.h"
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2018-07-19 14:10:48 +02:00
|
|
|
#include <qmath.h>
|
2018-08-03 08:15:28 +02:00
|
|
|
#include <qsggeometry.h>
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
using namespace QskVertex;
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
TopLeft = Qt::TopLeftCorner,
|
|
|
|
TopRight = Qt::TopRightCorner,
|
|
|
|
BottomLeft = Qt::BottomLeftCorner,
|
|
|
|
BottomRight = Qt::BottomRightCorner
|
|
|
|
};
|
|
|
|
|
2022-02-04 16:11:11 +01:00
|
|
|
int additionalGradientStops( const QskGradient& gradient )
|
|
|
|
{
|
2022-10-24 17:29:39 +02:00
|
|
|
return qMax( 0, gradient.stepCount() - 1 );
|
2022-02-04 16:11:11 +01:00
|
|
|
}
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
class BorderValuesUniform
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
2022-12-28 19:39:08 +01:00
|
|
|
inline BorderValuesUniform( const QskRoundedRectRenderer::Metrics& metrics )
|
2018-08-03 08:15:28 +02:00
|
|
|
: m_corner( metrics.corner[ 0 ] )
|
|
|
|
, m_dx1( m_corner.radiusInnerX )
|
|
|
|
, m_dy1( m_corner.radiusInnerY )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void setAngle( qreal cos, qreal sin )
|
|
|
|
{
|
|
|
|
if ( !m_corner.isCropped )
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
m_dx1 = cos * m_corner.radiusInnerX;
|
2017-10-17 17:29:02 +02:00
|
|
|
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; }
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
private:
|
2022-12-28 19:39:08 +01:00
|
|
|
const QskRoundedRectRenderer::Metrics::Corner& m_corner;
|
2017-10-17 17:29:02 +02:00
|
|
|
qreal m_dx1, m_dy1, m_dx2, m_dy2;
|
|
|
|
};
|
|
|
|
|
2022-12-28 19:39:08 +01:00
|
|
|
class BorderValuesMulti
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
2022-12-28 19:39:08 +01:00
|
|
|
inline BorderValuesMulti( const QskRoundedRectRenderer::Metrics& metrics )
|
2018-08-03 08:15:28 +02:00
|
|
|
: m_uniform( metrics.isRadiusRegular )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
|
|
|
for ( int i = 0; i < 4; i++ )
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
const auto& c = metrics.corner[ i ];
|
|
|
|
auto& v = m_inner[ i ];
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
m_outer[ i ].x0 = m_outer[ i ].y0 = 0.0;
|
|
|
|
m_outer[ i ].rx = c.radiusX;
|
|
|
|
m_outer[ i ].ry = c.radiusY;
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void setAngle( qreal cos, qreal sin )
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
m_inner[ 0 ].setAngle( cos, sin );
|
|
|
|
m_inner[ 1 ].setAngle( cos, sin );
|
|
|
|
m_inner[ 2 ].setAngle( cos, sin );
|
|
|
|
m_inner[ 3 ].setAngle( cos, sin );
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
m_outer[ 0 ].setAngle( cos, sin );
|
2017-10-17 17:29:02 +02:00
|
|
|
if ( !m_uniform )
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
m_outer[ 1 ].setAngle( cos, sin );
|
|
|
|
m_outer[ 2 ].setAngle( cos, sin );
|
|
|
|
m_outer[ 3 ].setAngle( cos, sin );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-04 13:42:16 +01:00
|
|
|
inline qreal dx1( int pos ) const { return m_inner[ pos].dx; }
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
inline qreal dy1( int pos ) const { return m_inner[ pos ].dy; }
|
2019-01-04 13:42:16 +01:00
|
|
|
|
2017-10-17 17:29:02 +02:00
|
|
|
inline qreal dx2( int pos ) const
|
2018-08-03 08:15:28 +02:00
|
|
|
{ return m_uniform ? m_outer[ 0 ].dx : m_outer[ pos ].dx; }
|
2019-01-04 13:42:16 +01:00
|
|
|
|
2017-10-17 17:29:02 +02:00
|
|
|
inline qreal dy2( int pos ) const
|
2018-08-03 08:15:28 +02:00
|
|
|
{ return m_uniform ? m_outer[ 0 ].dy : m_outer[ pos ].dy; }
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
private:
|
2023-01-05 13:06:32 +01:00
|
|
|
const bool m_uniform;
|
|
|
|
|
2017-10-17 17:29:02 +02:00
|
|
|
class Values
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
2017-10-17 17:29:02 +02:00
|
|
|
inline void setAngle( qreal cos, qreal sin )
|
|
|
|
{
|
|
|
|
dx = x0 + cos * rx;
|
|
|
|
dy = y0 + sin * ry;
|
|
|
|
}
|
|
|
|
|
|
|
|
qreal dx, dy;
|
|
|
|
qreal x0, y0, rx, ry;
|
|
|
|
};
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
Values m_inner[ 4 ];
|
|
|
|
Values m_outer[ 4 ];
|
2017-10-17 17:29:02 +02:00
|
|
|
};
|
|
|
|
|
2023-01-10 10:20:30 +01:00
|
|
|
class Vector1D
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
2023-01-10 10:20:30 +01:00
|
|
|
Vector1D() = default;
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2023-01-10 10:20:30 +01:00
|
|
|
inline constexpr Vector1D( qreal origin, qreal length )
|
|
|
|
: origin( origin )
|
|
|
|
, length( length )
|
|
|
|
{
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
|
2023-01-10 10:20:30 +01:00
|
|
|
inline qreal valueAt( qreal t ) const
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
2023-01-10 10:20:30 +01:00
|
|
|
return origin + t * length;
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
|
2023-01-10 10:20:30 +01:00
|
|
|
qreal origin = 0.0;
|
|
|
|
qreal length = 0.0;
|
2017-10-17 17:29:02 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
2023-01-10 10:20:30 +01:00
|
|
|
/*
|
|
|
|
A contour iterator for vertical and horizontal linear gradients.
|
|
|
|
The radii in direction of the gradient need to match at the
|
|
|
|
opening and at the closing sides,
|
|
|
|
*/
|
2023-01-05 17:18:34 +01:00
|
|
|
class HVRectEllipseIterator
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
2023-01-05 17:18:34 +01:00
|
|
|
HVRectEllipseIterator(
|
|
|
|
const QskRoundedRectRenderer::Metrics& metrics, const QLineF& vector )
|
2023-01-10 10:20:30 +01:00
|
|
|
: m_vertical( vector.x1() == vector.x2() )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
2023-01-10 10:20:30 +01:00
|
|
|
const int* c;
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2023-01-05 17:18:34 +01:00
|
|
|
if ( m_vertical )
|
|
|
|
{
|
2023-01-10 10:20:30 +01:00
|
|
|
static const int cV[] = { TopLeft, TopRight, BottomLeft, BottomRight };
|
|
|
|
c = cV;
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2023-01-05 17:18:34 +01:00
|
|
|
m_pos0 = metrics.innerQuad.top;
|
|
|
|
m_size = metrics.innerQuad.height;
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2023-01-05 17:18:34 +01:00
|
|
|
m_t = m_pos0 + vector.y1() * m_size;
|
|
|
|
m_dt = vector.dy() * m_size;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-01-10 10:20:30 +01:00
|
|
|
static const int cH[] = { TopLeft, BottomLeft, TopRight, BottomRight };
|
|
|
|
c = cH;
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2023-01-05 17:18:34 +01:00
|
|
|
m_pos0 = metrics.innerQuad.left;
|
|
|
|
m_size = metrics.innerQuad.width;
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2023-01-05 17:18:34 +01:00
|
|
|
m_t = m_pos0 + vector.x1() * m_size;
|
|
|
|
m_dt = vector.dx() * m_size;
|
|
|
|
}
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2023-01-10 10:20:30 +01:00
|
|
|
const auto& mc1 = metrics.corner[ c[0] ];
|
|
|
|
const auto& mc2 = metrics.corner[ c[1] ];
|
|
|
|
const auto& mc3 = ( mc1.stepCount >= mc2.stepCount ) ? mc1 : mc2;
|
|
|
|
|
|
|
|
const auto& mc4 = metrics.corner[ c[2] ];
|
|
|
|
const auto& mc5 = metrics.corner[ c[3] ];
|
|
|
|
const auto& mc6 = ( mc4.stepCount >= mc5.stepCount ) ? mc4 : mc5;
|
|
|
|
|
|
|
|
m_vector[0] = vectorAt( !m_vertical, false, mc1 );
|
|
|
|
m_vector[1] = vectorAt( !m_vertical, true, mc2 );
|
|
|
|
m_vector[2] = vectorAt( m_vertical, false, mc3 );
|
|
|
|
|
|
|
|
m_vector[3] = vectorAt( !m_vertical, false, mc4 );
|
|
|
|
m_vector[4] = vectorAt( !m_vertical, true, mc5 );
|
|
|
|
m_vector[5] = vectorAt( m_vertical, true, mc6 );
|
|
|
|
|
|
|
|
m_stepCounts[0] = mc3.stepCount;
|
|
|
|
m_stepCounts[1] = mc6.stepCount;
|
|
|
|
|
|
|
|
m_v1.from = m_vector[0].valueAt( 1.0 );
|
|
|
|
m_v1.to = m_vector[1].valueAt( 1.0 );
|
2023-01-05 17:18:34 +01:00
|
|
|
m_v1.pos = m_pos0;
|
|
|
|
|
|
|
|
m_v2 = m_v1;
|
|
|
|
|
2023-01-10 10:20:30 +01:00
|
|
|
m_arcIterator.reset( m_stepCounts[0], false );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool advance()
|
|
|
|
{
|
2023-01-10 10:20:30 +01:00
|
|
|
auto v = m_vector;
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
if ( m_arcIterator.step() == m_arcIterator.stepCount() )
|
|
|
|
{
|
|
|
|
if ( m_arcIterator.isInverted() )
|
2023-01-05 17:18:34 +01:00
|
|
|
{
|
|
|
|
// we have finished the closing "corners"
|
2017-10-17 17:29:02 +02:00
|
|
|
return false;
|
2023-01-05 17:18:34 +01:00
|
|
|
}
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2023-01-10 10:20:30 +01:00
|
|
|
m_arcIterator.reset( m_stepCounts[1], true );
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2023-01-05 17:18:34 +01:00
|
|
|
const qreal pos1 = v[2].valueAt( 0.0 );
|
|
|
|
const qreal pos2 = v[5].valueAt( 0.0 );
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2023-01-05 17:18:34 +01:00
|
|
|
if ( pos1 < pos2 )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
2023-01-05 17:18:34 +01:00
|
|
|
// the real rectangle - between the rounded "corners "
|
|
|
|
m_v1 = m_v2;
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2023-01-05 17:18:34 +01:00
|
|
|
m_v2.from = v[3].valueAt( 1.0 );
|
|
|
|
m_v2.to = v[4].valueAt( 1.0 );
|
|
|
|
m_v2.pos = pos2;
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_arcIterator.increment();
|
|
|
|
|
2023-01-05 17:18:34 +01:00
|
|
|
m_v1 = m_v2;
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
if ( m_arcIterator.isInverted() )
|
2023-01-05 17:18:34 +01:00
|
|
|
v += 3;
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2023-01-05 17:18:34 +01:00
|
|
|
m_v2.from = v[0].valueAt( m_arcIterator.cos() );
|
|
|
|
m_v2.to = v[1].valueAt( m_arcIterator.cos() );
|
|
|
|
m_v2.pos = v[2].valueAt( m_arcIterator.sin() );
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2023-01-05 17:18:34 +01:00
|
|
|
return true;
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
|
2022-12-28 19:39:08 +01:00
|
|
|
inline bool setGradientLine( qreal value, Color color, ColoredLine* line )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
2023-01-05 17:18:34 +01:00
|
|
|
const auto pos = m_t + value * m_dt;
|
2022-12-28 19:39:08 +01:00
|
|
|
|
2023-01-05 17:18:34 +01:00
|
|
|
const qreal f = ( pos - m_v1.pos ) / ( m_v2.pos - m_v1.pos );
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2023-01-05 17:18:34 +01:00
|
|
|
const qreal v1 = m_v1.from + f * ( m_v2.from - m_v1.from );
|
|
|
|
const qreal v2 = m_v1.to + f * ( m_v2.to - m_v1.to );
|
|
|
|
|
|
|
|
setLine( v1, v2, pos, color, line );
|
2022-12-28 19:39:08 +01:00
|
|
|
return true;
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
|
2022-12-28 19:39:08 +01:00
|
|
|
inline void setContourLine( Color color, ColoredLine* line )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
2023-01-05 17:18:34 +01:00
|
|
|
setLine( m_v2.from, m_v2.to, m_v2.pos, color, line );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
inline qreal value() const
|
|
|
|
{
|
2023-01-05 17:18:34 +01:00
|
|
|
// coordinate translated into the gradiet vector
|
|
|
|
return ( m_v2.pos - m_t ) / m_dt;
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
|
2023-01-05 17:18:34 +01:00
|
|
|
private:
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2023-01-10 10:20:30 +01:00
|
|
|
inline Vector1D vectorAt( bool vertical, bool increasing,
|
|
|
|
const QskRoundedRectRenderer::Metrics::Corner& c ) const
|
|
|
|
{
|
|
|
|
qreal center, radius;
|
|
|
|
|
|
|
|
if ( vertical )
|
|
|
|
{
|
|
|
|
center = c.centerY;
|
|
|
|
radius = c.radiusInnerY;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
center = c.centerX;
|
|
|
|
radius = c.radiusInnerX;
|
|
|
|
}
|
|
|
|
|
|
|
|
const qreal f = increasing ? 1.0 : -1.0;
|
|
|
|
|
|
|
|
if ( radius < 0.0 )
|
|
|
|
{
|
|
|
|
center += radius * f;
|
|
|
|
radius = 0.0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
radius *= f;
|
|
|
|
}
|
|
|
|
|
|
|
|
return { center, radius };
|
|
|
|
}
|
|
|
|
|
2023-01-05 17:18:34 +01:00
|
|
|
inline void setLine( qreal from, qreal to, qreal pos,
|
|
|
|
Color color, ColoredLine* line )
|
|
|
|
{
|
|
|
|
if ( m_vertical )
|
|
|
|
line->setLine( from, pos, to, pos, color );
|
2017-10-17 17:29:02 +02:00
|
|
|
else
|
2023-01-05 17:18:34 +01:00
|
|
|
line->setLine( pos, from, pos, to, color );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
|
2023-01-05 17:18:34 +01:00
|
|
|
const bool m_vertical;
|
|
|
|
|
2023-01-10 10:20:30 +01:00
|
|
|
int m_stepCounts[2];
|
2017-10-17 17:29:02 +02:00
|
|
|
ArcIterator m_arcIterator;
|
2023-01-05 17:18:34 +01:00
|
|
|
|
2023-01-10 10:20:30 +01:00
|
|
|
/*
|
|
|
|
This iterator supports shapes, where we have the same radius in
|
|
|
|
direction of the gradient ( exception: one corner is not rounded ).
|
|
|
|
However we allow having different radii opposite to the direction
|
|
|
|
of the gradient. So we have 3 center/radius pairs to calculate the
|
|
|
|
interpolating contour lines at both ends ( opening/closing ).
|
|
|
|
*/
|
|
|
|
Vector1D m_vector[6];
|
|
|
|
|
|
|
|
/*
|
|
|
|
position of the previous and following contour line, so that
|
|
|
|
the positions of the gradiet lines in between can be calculated.
|
|
|
|
*/
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
qreal from, to; // opposite to the direction of the gradient
|
|
|
|
qreal pos; // in direction of the gradient
|
|
|
|
} m_v1, m_v2;
|
2023-01-05 17:18:34 +01:00
|
|
|
|
|
|
|
qreal m_pos0, m_size;
|
|
|
|
qreal m_t, m_dt; // to translate into gradient values
|
2017-10-17 17:29:02 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
class BorderMapNone
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
2017-10-17 17:29:02 +02:00
|
|
|
static inline constexpr Color colorAt( int ) { return Color(); }
|
2022-02-04 16:11:11 +01:00
|
|
|
inline QskGradient gradient() const { return QskGradient(); }
|
2017-10-17 17:29:02 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
class BorderMapSolid
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
2022-10-21 16:49:05 +02:00
|
|
|
inline BorderMapSolid( const QskBoxBorderColors& colors )
|
2022-11-16 13:46:37 +01:00
|
|
|
: m_color( colors.left().rgbStart() )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
inline Color colorAt( int ) const { return m_color; }
|
2022-02-04 16:11:11 +01:00
|
|
|
inline QskGradient gradient() const { return QskGradient(); }
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
const Color m_color;
|
|
|
|
};
|
|
|
|
|
|
|
|
class BorderMapGradient
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
2022-12-28 19:39:08 +01:00
|
|
|
inline BorderMapGradient( int stepCount,
|
|
|
|
const QskGradient& gradient1, const QskGradient& gradient2 )
|
2018-08-03 08:15:28 +02:00
|
|
|
: m_stepCount( stepCount )
|
2022-11-16 13:46:37 +01:00
|
|
|
, m_color1( gradient1.rgbStart() )
|
|
|
|
, m_color2( gradient2.rgbEnd() )
|
2022-10-21 16:49:05 +02:00
|
|
|
, m_gradient( gradient2 )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
inline Color colorAt( int step ) const
|
|
|
|
{
|
|
|
|
return m_color1.interpolatedTo( m_color2, step / m_stepCount );
|
|
|
|
}
|
|
|
|
|
2022-02-04 16:18:14 +01:00
|
|
|
inline const QskGradient& gradient() const
|
2022-02-04 16:11:11 +01:00
|
|
|
{
|
|
|
|
return m_gradient;
|
|
|
|
}
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
private:
|
2017-10-17 17:29:02 +02:00
|
|
|
const qreal m_stepCount;
|
|
|
|
const Color m_color1, m_color2;
|
2022-02-04 16:11:11 +01:00
|
|
|
const QskGradient m_gradient;
|
2017-10-17 17:29:02 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
template< class Line, class BorderValues >
|
|
|
|
class Stroker
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
2022-12-28 19:39:08 +01:00
|
|
|
inline Stroker( const QskRoundedRectRenderer::Metrics& metrics )
|
2018-08-03 08:15:28 +02:00
|
|
|
: m_metrics( metrics )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-10-20 09:29:20 +02:00
|
|
|
inline void setAdditionalLine(
|
|
|
|
float x11, float y11, float x12, float y12,
|
|
|
|
float dx1, float dy1, float dx2, float dy2,
|
|
|
|
const QskGradientStop& stop, Line* line )
|
|
|
|
{
|
|
|
|
const auto pos = 1.0 - stop.position();
|
|
|
|
|
|
|
|
const float x1 = x11 + pos * dx1;
|
|
|
|
const float y1 = y11 + pos * dy1;
|
|
|
|
const float x2 = x12 + pos * dx2;
|
|
|
|
const float y2 = y12 + pos * dy2;
|
|
|
|
|
2022-11-14 09:01:07 +01:00
|
|
|
line->setLine( x1, y1, x2, y2, stop.rgb() );
|
2022-10-20 09:29:20 +02:00
|
|
|
}
|
|
|
|
|
2022-03-23 11:54:34 +01:00
|
|
|
void addAdditionalLines(
|
|
|
|
float x11, float y11, float x12, float y12, // start line
|
|
|
|
float x21, float y21, float x22, float y22, // end line
|
|
|
|
const QskGradient& gradient, Line* lines )
|
2022-02-04 16:11:11 +01:00
|
|
|
{
|
2022-10-20 09:29:20 +02:00
|
|
|
const float dx1 = x21 - x11;
|
|
|
|
const float dy1 = y21 - y11;
|
|
|
|
const float dx2 = x22 - x12;
|
|
|
|
const float dy2 = y22 - y12;
|
|
|
|
|
|
|
|
const auto stops = gradient.stops();
|
2022-02-04 16:11:11 +01:00
|
|
|
|
2022-10-20 09:29:20 +02:00
|
|
|
auto line = lines + additionalGradientStops( gradient );
|
2022-02-04 16:11:11 +01:00
|
|
|
|
2022-10-24 17:29:39 +02:00
|
|
|
{
|
2022-12-28 19:39:08 +01:00
|
|
|
const auto& stop = stops.first();
|
2022-10-24 17:29:39 +02:00
|
|
|
|
|
|
|
if ( stop.position() > 0.0 )
|
|
|
|
setAdditionalLine( x11, y11, x12, y12, dx1, dy1, dx2, dy2, stop, line-- );
|
|
|
|
}
|
|
|
|
|
2022-10-20 09:29:20 +02:00
|
|
|
for( int i = 1; i < stops.count() - 1; i++ )
|
2022-02-04 16:11:11 +01:00
|
|
|
{
|
2022-10-20 09:29:20 +02:00
|
|
|
setAdditionalLine( x11, y11, x12, y12, dx1, dy1, dx2, dy2, stops[i], line-- );
|
2022-02-04 16:11:11 +01:00
|
|
|
}
|
2022-10-24 17:29:39 +02:00
|
|
|
|
|
|
|
{
|
2022-12-28 19:39:08 +01:00
|
|
|
const auto& stop = stops.last();
|
2022-10-24 17:29:39 +02:00
|
|
|
|
|
|
|
if ( stop.position() < 1.0 )
|
|
|
|
{
|
|
|
|
setAdditionalLine( x11, y11, x12, y12, dx1, dy1, dx2, dy2, stop, line-- );
|
|
|
|
}
|
|
|
|
}
|
2022-02-04 16:11:11 +01:00
|
|
|
}
|
|
|
|
|
2017-10-17 17:29:02 +02:00
|
|
|
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 ...
|
2018-08-03 08:15:28 +02:00
|
|
|
const int stepCount = c[ 0 ].stepCount;
|
2017-10-17 17:29:02 +02:00
|
|
|
#endif
|
|
|
|
|
2019-01-04 13:42:16 +01:00
|
|
|
Line* linesBR, * linesTR, * linesTL, * linesBL;
|
2017-10-18 19:51:29 +02:00
|
|
|
linesBR = linesTR = linesTL = linesBL = nullptr;
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
const int numCornerLines = stepCount + 1;
|
|
|
|
int numFillLines = fillLines ? 2 * numCornerLines : 0;
|
|
|
|
|
|
|
|
if ( orientation == Qt::Vertical )
|
|
|
|
{
|
|
|
|
if ( borderLines )
|
|
|
|
{
|
|
|
|
linesBR = borderLines;
|
2022-02-04 16:18:14 +01:00
|
|
|
|
2022-02-04 16:11:11 +01:00
|
|
|
linesTR = linesBR + numCornerLines
|
|
|
|
+ additionalGradientStops( borderMapBR.gradient() );
|
2022-02-04 16:18:14 +01:00
|
|
|
|
2022-02-04 16:11:11 +01:00
|
|
|
linesTL = linesTR + numCornerLines
|
|
|
|
+ additionalGradientStops( borderMapTR.gradient() );
|
2022-02-04 16:18:14 +01:00
|
|
|
|
2022-02-04 16:11:11 +01:00
|
|
|
linesBL = linesTL + numCornerLines
|
|
|
|
+ additionalGradientStops( borderMapTL.gradient() );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( fillLines )
|
|
|
|
{
|
|
|
|
if ( m_metrics.centerQuad.top >= m_metrics.centerQuad.bottom )
|
|
|
|
numFillLines--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( borderLines )
|
|
|
|
{
|
|
|
|
linesTR = borderLines + 1;
|
2022-02-04 16:18:14 +01:00
|
|
|
|
2022-02-04 16:11:11 +01:00
|
|
|
linesTL = linesTR + numCornerLines
|
|
|
|
+ additionalGradientStops( borderMapTR.gradient() );
|
2022-02-04 16:18:14 +01:00
|
|
|
|
2022-02-04 16:11:11 +01:00
|
|
|
linesBL = linesTL + numCornerLines
|
|
|
|
+ additionalGradientStops( borderMapTL.gradient() );
|
2022-02-04 16:18:14 +01:00
|
|
|
|
2022-02-04 16:11:11 +01:00
|
|
|
linesBR = linesBL + numCornerLines
|
|
|
|
+ additionalGradientStops( borderMapBL.gradient() );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
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 ),
|
2017-10-17 17:29:02 +02:00
|
|
|
borderMapTL.colorAt( j ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
constexpr auto corner = TopRight;
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
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 ),
|
2017-10-17 17:29:02 +02:00
|
|
|
borderMapTR.colorAt( k ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
constexpr auto corner = BottomLeft;
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
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 ),
|
2017-10-17 17:29:02 +02:00
|
|
|
borderMapBL.colorAt( k ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
constexpr auto corner = BottomRight;
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
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 ),
|
2017-10-17 17:29:02 +02:00
|
|
|
borderMapBR.colorAt( j ) );
|
|
|
|
}
|
2022-02-04 16:11:11 +01:00
|
|
|
|
|
|
|
// at the beginning and end of the loop we can add
|
|
|
|
// additional lines for border gradients:
|
|
|
|
|
|
|
|
if( j == 0 )
|
|
|
|
{
|
|
|
|
if( additionalGradientStops( borderMapTR.gradient() ) > 0 )
|
|
|
|
{
|
|
|
|
float x1TR = c[ TopRight ].centerX + v.dx1( TopRight ),
|
2022-03-23 11:54:34 +01:00
|
|
|
y1TR = c[ TopRight ].centerY - v.dy1( TopRight ),
|
|
|
|
x2TR = c[ TopRight ].centerX + v.dx2( TopRight ),
|
|
|
|
y2TR = c[ TopRight ].centerY - v.dy2( TopRight ),
|
|
|
|
|
|
|
|
x1TL = c[ TopLeft ].centerX - v.dx1( TopLeft ),
|
|
|
|
y1TL = c[ TopLeft ].centerY - v.dy1( TopLeft ),
|
|
|
|
x2TL = c[ TopLeft ].centerX - v.dx2( TopLeft ),
|
|
|
|
y2TL = c[ TopLeft ].centerY - v.dy2( TopLeft );
|
|
|
|
|
|
|
|
addAdditionalLines(
|
|
|
|
x1TR, y1TR, x2TR, y2TR,
|
|
|
|
x1TL, y1TL, x2TL, y2TL,
|
|
|
|
borderMapTR.gradient(), linesTR + k );
|
2022-02-04 16:11:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if( additionalGradientStops( borderMapBL.gradient() ) > 0 )
|
|
|
|
{
|
|
|
|
float x1BL = c[ BottomLeft ].centerX - v.dx1( BottomLeft ),
|
2022-03-23 11:54:34 +01:00
|
|
|
y1BL = c[ BottomLeft ].centerY + v.dy1( BottomLeft ),
|
|
|
|
x2BL = c[ BottomLeft ].centerX - v.dx2( BottomLeft ),
|
|
|
|
y2BL = c[ BottomLeft ].centerY + v.dy2( BottomLeft ),
|
|
|
|
|
|
|
|
x1BR = c[ BottomRight ].centerX + v.dx1( BottomRight ),
|
|
|
|
y1BR = c[ BottomRight ].centerY + v.dy1( BottomRight ),
|
|
|
|
x2BR = c[ BottomRight ].centerX + v.dx2( BottomRight ),
|
|
|
|
y2BR = c[ BottomRight ].centerY + v.dy2( BottomRight );
|
|
|
|
|
|
|
|
addAdditionalLines(
|
|
|
|
x1BL, y1BL, x2BL, y2BL,
|
|
|
|
x1BR, y1BR, x2BR, y2BR,
|
|
|
|
borderMapBL.gradient(), linesBL + k );
|
2022-02-04 16:11:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( j == numCornerLines - 1 )
|
|
|
|
{
|
|
|
|
if( additionalGradientStops( borderMapTL.gradient() ) > 0 )
|
|
|
|
{
|
|
|
|
float x1TL = c[ TopLeft ].centerX - v.dx1( TopLeft ),
|
2022-03-23 11:54:34 +01:00
|
|
|
y1TL = c[ TopLeft ].centerY - v.dy1( TopLeft ),
|
|
|
|
x2TL = c[ TopLeft ].centerX - v.dx2( TopLeft ),
|
|
|
|
y2TL = c[ TopLeft ].centerY - v.dy2( TopLeft ),
|
|
|
|
|
|
|
|
x1BL = c[ BottomLeft ].centerX - v.dx1( BottomLeft ),
|
|
|
|
y1BL = c[ BottomLeft ].centerY + v.dy1( BottomLeft ),
|
|
|
|
x2BL = c[ BottomLeft ].centerX - v.dx2( BottomLeft ),
|
|
|
|
y2BL = c[ BottomLeft ].centerY + v.dy2( BottomLeft );
|
|
|
|
|
|
|
|
addAdditionalLines(
|
|
|
|
x1TL, y1TL, x2TL, y2TL,
|
|
|
|
x1BL, y1BL, x2BL, y2BL,
|
|
|
|
borderMapTL.gradient(), linesTL + j );
|
2022-02-04 16:11:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if( additionalGradientStops( borderMapBR.gradient() ) > 0 )
|
|
|
|
{
|
|
|
|
float x1BR = c[ BottomRight ].centerX + v.dx1( BottomRight ),
|
2022-03-23 11:54:34 +01:00
|
|
|
y1BR = c[ BottomRight ].centerY + v.dy1( BottomRight ),
|
|
|
|
x2BR = c[ BottomRight ].centerX + v.dx2( BottomRight ),
|
|
|
|
y2BR = c[ BottomRight ].centerY + v.dy2( BottomRight ),
|
|
|
|
|
|
|
|
x1TR = c[ TopRight ].centerX + v.dx1( TopRight ),
|
|
|
|
y1TR = c[ TopRight ].centerY - v.dy1( TopRight ),
|
|
|
|
x2TR = c[ TopRight ].centerX + v.dx2( TopRight ),
|
|
|
|
y2TR = c[ TopRight ].centerY - v.dy2( TopRight );
|
|
|
|
|
|
|
|
addAdditionalLines(
|
|
|
|
x1BR, y1BR, x2BR, y2BR,
|
|
|
|
x1TR, y1TR, x2TR, y2TR,
|
|
|
|
borderMapBR.gradient(), linesBR + j );
|
2022-02-04 16:11:11 +01:00
|
|
|
}
|
|
|
|
}
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( fillLines )
|
|
|
|
{
|
|
|
|
const auto& ri = m_metrics.innerQuad;
|
|
|
|
|
|
|
|
if ( orientation == Qt::Vertical )
|
|
|
|
{
|
|
|
|
const int j = it.step();
|
|
|
|
const int k = numFillLines - it.step() - 1;
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
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 );
|
2017-10-17 17:29:02 +02:00
|
|
|
const auto c1 = fillMap.colorAt( ( y1 - ri.top ) / ri.height );
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
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 );
|
2017-10-17 17:29:02 +02:00
|
|
|
const auto c2 = fillMap.colorAt( ( y2 - ri.top ) / ri.height );
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
fillLines[ j ].setLine( x11, y1, x12, y1, c1 );
|
|
|
|
fillLines[ k ].setLine( x21, y2, x22, y2, c2 );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const int j = stepCount - it.step();
|
|
|
|
const int k = numFillLines - 1 - stepCount + it.step();
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
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 );
|
2017-10-17 17:29:02 +02:00
|
|
|
const auto c1 = fillMap.colorAt( ( x1 - ri.left ) / ri.width );
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
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 );
|
2017-10-17 17:29:02 +02:00
|
|
|
const auto c2 = fillMap.colorAt( ( x2 - ri.left ) / ri.width );
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
fillLines[ j ].setLine( x1, y11, x1, y12, c1 );
|
|
|
|
fillLines[ k ].setLine( x2, y21, x2, y22, c2 );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 1
|
|
|
|
if ( borderLines )
|
|
|
|
{
|
2022-02-04 16:11:11 +01:00
|
|
|
const int additionalStops =
|
|
|
|
additionalGradientStops( borderMapBR.gradient() )
|
|
|
|
+ additionalGradientStops( borderMapTR.gradient() )
|
|
|
|
+ additionalGradientStops( borderMapTL.gradient() )
|
|
|
|
+ additionalGradientStops( borderMapBL.gradient() );
|
2022-02-04 16:18:14 +01:00
|
|
|
|
2022-02-04 16:11:11 +01:00
|
|
|
const int k = 4 * numCornerLines + additionalStops;
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
if ( orientation == Qt::Vertical )
|
2018-08-03 08:15:28 +02:00
|
|
|
borderLines[ k ] = borderLines[ 0 ];
|
2017-10-17 17:29:02 +02:00
|
|
|
else
|
2018-08-03 08:15:28 +02:00
|
|
|
borderLines[ 0 ] = borderLines[ k ];
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
private:
|
2022-12-28 19:39:08 +01:00
|
|
|
const QskRoundedRectRenderer::Metrics& m_metrics;
|
2017-10-17 17:29:02 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-11-13 17:22:09 +01:00
|
|
|
static inline Qt::Orientation qskQtOrientation( const QskGradient& gradient )
|
2021-10-19 06:44:17 +02:00
|
|
|
{
|
2022-11-13 17:22:09 +01:00
|
|
|
return gradient.linearDirection().isVertical() ? Qt::Vertical : Qt::Horizontal;
|
2021-10-19 06:44:17 +02:00
|
|
|
}
|
|
|
|
|
2017-10-17 17:29:02 +02:00
|
|
|
static inline int qskFillLineCount(
|
2022-12-28 19:39:08 +01:00
|
|
|
const QskRoundedRectRenderer::Metrics& metrics, const QskGradient& gradient )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
|
|
|
if ( !gradient.isVisible() )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
int lineCount = 0;
|
|
|
|
|
2022-11-13 17:22:09 +01:00
|
|
|
const auto dir = gradient.linearDirection();
|
|
|
|
|
|
|
|
if ( dir.isVertical() )
|
2022-10-25 18:19:32 +02:00
|
|
|
{
|
|
|
|
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--;
|
|
|
|
}
|
2022-11-13 17:22:09 +01:00
|
|
|
else if ( dir.isHorizontal() )
|
2022-10-25 18:19:32 +02:00
|
|
|
{
|
|
|
|
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--;
|
|
|
|
}
|
|
|
|
else
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
2023-01-05 13:06:32 +01:00
|
|
|
lineCount += 2 * ( metrics.corner[ 0 ].stepCount + 1 );
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2022-10-25 18:17:21 +02:00
|
|
|
if ( metrics.centerQuad.left >= metrics.centerQuad.right )
|
|
|
|
lineCount--;
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2022-10-25 18:17:21 +02:00
|
|
|
if ( metrics.centerQuad.top >= metrics.centerQuad.bottom )
|
|
|
|
lineCount--;
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2022-10-25 18:17:21 +02:00
|
|
|
/*
|
|
|
|
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
|
|
|
|
*/
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2022-10-25 18:17:21 +02:00
|
|
|
lineCount *= 2; // a real ellipse could be done with lineCount lines: TODO ...
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
#if 1
|
2022-10-25 18:17:21 +02:00
|
|
|
/*
|
|
|
|
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.
|
|
|
|
*/
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2022-10-25 18:17:21 +02:00
|
|
|
lineCount++; // happens in a corner case - needs to be understood: TODO
|
2017-10-17 17:29:02 +02:00
|
|
|
#endif
|
2022-10-25 18:17:21 +02:00
|
|
|
}
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
// adding vertexes for the stops - beside the first/last
|
|
|
|
|
|
|
|
if ( !gradient.isMonochrome() )
|
2022-10-24 17:29:39 +02:00
|
|
|
lineCount += gradient.stepCount() - 1;
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
return lineCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
template< class Line, class BorderMap, class FillMap >
|
2018-08-03 08:15:28 +02:00
|
|
|
static inline void qskRenderLines(
|
2022-12-28 19:39:08 +01:00
|
|
|
const QskRoundedRectRenderer::Metrics& metrics,
|
2017-10-17 17:29:02 +02:00
|
|
|
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 );
|
2018-08-03 08:15:28 +02:00
|
|
|
stroker.createLines( orientation, borderLines,
|
|
|
|
borderMapTL, borderMapTR, borderMapBL, borderMapBR,
|
|
|
|
fillLines, fillMap );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-12-28 19:39:08 +01:00
|
|
|
Stroker< Line, BorderValuesMulti > stroker( metrics );
|
2018-08-03 08:15:28 +02:00
|
|
|
stroker.createLines( orientation, borderLines,
|
|
|
|
borderMapTL, borderMapTR, borderMapBL, borderMapBR,
|
|
|
|
fillLines, fillMap );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template< class Line, class BorderMap, class FillMap >
|
2018-08-03 08:15:28 +02:00
|
|
|
static inline void qskRenderLines(
|
2022-12-28 19:39:08 +01:00
|
|
|
const QskRoundedRectRenderer::Metrics& metrics, Qt::Orientation orientation,
|
2018-08-03 08:15:28 +02:00
|
|
|
Line* borderLines, const BorderMap& borderMap, Line* fillLines,
|
|
|
|
const FillMap& fillMap )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
qskRenderLines( metrics, orientation, borderLines,
|
|
|
|
borderMap, borderMap, borderMap, borderMap, fillLines, fillMap );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
template< class Line, class BorderMap >
|
2018-08-03 08:15:28 +02:00
|
|
|
static inline void qskRenderBorderLines(
|
2022-12-28 19:39:08 +01:00
|
|
|
const QskRoundedRectRenderer::Metrics& metrics,
|
2017-10-17 17:29:02 +02:00
|
|
|
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 >
|
2018-08-03 08:15:28 +02:00
|
|
|
static inline void qskRenderBorderLines(
|
2022-12-28 19:39:08 +01:00
|
|
|
const QskRoundedRectRenderer::Metrics& metrics,
|
2017-10-17 17:29:02 +02:00
|
|
|
Qt::Orientation orientation, Line* lines, const BorderMap& borderMap )
|
|
|
|
{
|
|
|
|
qskRenderBorderLines( metrics, orientation, lines,
|
|
|
|
borderMap, borderMap, borderMap, borderMap );
|
|
|
|
}
|
|
|
|
|
|
|
|
template< class Line, class FillMap >
|
2022-12-28 19:39:08 +01:00
|
|
|
static inline void qskRenderFillLines( const QskRoundedRectRenderer::Metrics& metrics,
|
2017-10-17 17:29:02 +02:00
|
|
|
Qt::Orientation orientation, Line* lines, const FillMap& fillMap )
|
|
|
|
{
|
|
|
|
qskRenderLines( metrics, orientation,
|
|
|
|
static_cast< Line* >( nullptr ), BorderMapNone(), lines, fillMap );
|
|
|
|
}
|
|
|
|
|
2022-12-28 19:39:08 +01:00
|
|
|
static inline void qskRenderBorder( const QskRoundedRectRenderer::Metrics& metrics,
|
2017-10-17 17:29:02 +02:00
|
|
|
Qt::Orientation orientation, const QskBoxBorderColors& colors, ColoredLine* line )
|
|
|
|
{
|
|
|
|
if ( colors.isMonochrome() )
|
|
|
|
{
|
2022-10-21 16:49:05 +02:00
|
|
|
qskRenderBorderLines( metrics, orientation,
|
|
|
|
line, BorderMapSolid( colors ) );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
const int stepCount = metrics.corner[ 0 ].stepCount;
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
qskRenderBorderLines( metrics, orientation, line,
|
2022-10-21 16:49:05 +02:00
|
|
|
BorderMapGradient( stepCount, colors.top(), colors.left() ),
|
|
|
|
BorderMapGradient( stepCount, colors.right(), colors.top() ),
|
|
|
|
BorderMapGradient( stepCount, colors.left(), colors.bottom() ),
|
|
|
|
BorderMapGradient( stepCount, colors.bottom(), colors.right() ) );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
static inline void qskRenderFillRandom(
|
2022-12-28 19:39:08 +01:00
|
|
|
const QskRoundedRectRenderer::Metrics& metrics,
|
2022-11-13 17:22:09 +01:00
|
|
|
const QskGradient& gradient, ColoredLine* line )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
2022-11-16 14:23:47 +01:00
|
|
|
// here we know that: gradient.stepCount() <= 1 !
|
|
|
|
|
2021-10-19 06:44:17 +02:00
|
|
|
const auto orientation = qskQtOrientation( gradient );
|
2020-08-05 14:02:03 +02:00
|
|
|
|
2017-10-17 17:29:02 +02:00
|
|
|
if ( gradient.isMonochrome() )
|
|
|
|
{
|
2022-11-16 14:23:47 +01:00
|
|
|
const ColorMapSolid map( gradient );
|
2020-08-05 14:02:03 +02:00
|
|
|
qskRenderFillLines( metrics, orientation, line, map );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-11-16 14:23:47 +01:00
|
|
|
const ColorMapGradient map( gradient );
|
2017-10-17 17:29:02 +02:00
|
|
|
qskRenderFillLines( metrics, orientation, line, map );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void qskRenderBoxRandom(
|
2022-12-28 19:39:08 +01:00
|
|
|
const QskRoundedRectRenderer::Metrics& metrics, const QskBoxBorderColors& borderColors,
|
2022-11-13 17:22:09 +01:00
|
|
|
const QskGradient& gradient, ColoredLine* fillLine, ColoredLine* borderLine )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
2022-11-16 14:23:47 +01:00
|
|
|
// here we know that: gradient.stepCount() <= 1 !
|
|
|
|
|
2017-10-17 17:29:02 +02:00
|
|
|
const auto& bc = borderColors;
|
|
|
|
|
|
|
|
if ( bc.isMonochrome() )
|
|
|
|
{
|
2022-10-21 16:49:05 +02:00
|
|
|
const BorderMapSolid borderMap( bc );
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
if ( gradient.isMonochrome() )
|
|
|
|
{
|
2022-11-16 14:23:47 +01:00
|
|
|
const ColorMapSolid fillMap( gradient );
|
2017-10-17 17:29:02 +02:00
|
|
|
qskRenderLines( metrics, Qt::Vertical, borderLine, borderMap, fillLine, fillMap );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-10-19 06:44:17 +02:00
|
|
|
const auto orientation = qskQtOrientation( gradient );
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2022-11-16 14:23:47 +01:00
|
|
|
const ColorMapGradient fillMap( gradient );
|
2017-10-17 17:29:02 +02:00
|
|
|
qskRenderLines( metrics, orientation, borderLine, borderMap, fillLine, fillMap );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
const int n = metrics.corner[ 0 ].stepCount;
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2022-04-16 16:01:40 +02:00
|
|
|
const auto& left = bc.left();
|
|
|
|
const auto& top = bc.top();
|
|
|
|
const auto& right = bc.right();
|
|
|
|
const auto& bottom = bc.bottom();
|
2022-03-23 11:54:34 +01:00
|
|
|
|
2022-10-21 16:49:05 +02:00
|
|
|
const BorderMapGradient tl( n, top, left );
|
|
|
|
const BorderMapGradient tr( n, right, top );
|
|
|
|
const BorderMapGradient bl( n, left, bottom );
|
|
|
|
const BorderMapGradient br( n, bottom, right );
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
if ( gradient.isMonochrome() )
|
|
|
|
{
|
2022-11-16 14:23:47 +01:00
|
|
|
const ColorMapSolid fillMap( gradient );
|
2017-10-17 17:29:02 +02:00
|
|
|
qskRenderLines( metrics, Qt::Vertical, borderLine, tl, tr, bl, br, fillLine, fillMap );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-10-19 06:44:17 +02:00
|
|
|
const auto orientation = qskQtOrientation( gradient );
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2022-11-16 14:23:47 +01:00
|
|
|
const ColorMapGradient fillMap( gradient );
|
2017-10-17 17:29:02 +02:00
|
|
|
qskRenderLines( metrics, orientation, borderLine, tl, tr, bl, br, fillLine, fillMap );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
static inline void qskRenderFillOrdered(
|
2022-12-28 19:39:08 +01:00
|
|
|
const QskRoundedRectRenderer::Metrics& metrics,
|
2023-01-05 13:06:32 +01:00
|
|
|
const QskGradient& gradient, int lineCount, ColoredLine* lines )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
The algo for irregular radii at opposite corners is not yet
|
|
|
|
implemented TODO ...
|
|
|
|
*/
|
|
|
|
|
2023-01-05 17:18:34 +01:00
|
|
|
const auto dir = gradient.linearDirection();
|
|
|
|
|
|
|
|
HVRectEllipseIterator it( metrics, dir.vector() );
|
|
|
|
QskVertex::fillOrdered( it, gradient, lineCount,lines );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
|
2022-12-28 19:39:08 +01:00
|
|
|
QskRoundedRectRenderer::Metrics::Metrics( const QRectF& rect,
|
2018-08-03 08:15:28 +02:00
|
|
|
const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& border )
|
|
|
|
: outerQuad( rect )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
|
|
|
isRadiusRegular = shape.isRectellipse();
|
|
|
|
|
|
|
|
for ( int i = 0; i < 4; i++ )
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
auto& c = corner[ i ];
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
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 ) );
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
switch ( i )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
centerQuad.left = qMax( corner[ TopLeft ].centerX,
|
2017-10-17 17:29:02 +02:00
|
|
|
corner[ BottomLeft ].centerX );
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
centerQuad.right = qMin( corner[ TopRight ].centerX,
|
2017-10-17 17:29:02 +02:00
|
|
|
corner[ BottomRight ].centerX );
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
centerQuad.top = qMax( corner[ TopLeft ].centerY,
|
2017-10-17 17:29:02 +02:00
|
|
|
corner[ TopRight ].centerY );
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
centerQuad.bottom = qMin( corner[ BottomLeft ].centerY,
|
2017-10-17 17:29:02 +02:00
|
|
|
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++ )
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
auto& c = corner[ i ];
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
switch ( i )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
|
|
|
case TopLeft:
|
|
|
|
{
|
|
|
|
c.radiusInnerX = c.radiusX - borderLeft;
|
|
|
|
c.radiusInnerY = c.radiusY - borderTop;
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
c.isCropped = ( c.centerX <= innerQuad.left ) ||
|
|
|
|
( c.centerY <= innerQuad.top );
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TopRight:
|
|
|
|
{
|
|
|
|
c.radiusInnerX = c.radiusX - borderRight;
|
|
|
|
c.radiusInnerY = c.radiusY - borderTop;
|
|
|
|
|
2019-01-04 13:42:16 +01:00
|
|
|
c.isCropped = ( c.centerX >= innerQuad.right ) ||
|
2018-08-03 08:15:28 +02:00
|
|
|
( c.centerY <= innerQuad.top );
|
2017-10-17 17:29:02 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case BottomLeft:
|
|
|
|
{
|
|
|
|
c.radiusInnerX = c.radiusX - borderLeft;
|
|
|
|
c.radiusInnerY = c.radiusY - borderBottom;
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
c.isCropped = ( c.centerX <= innerQuad.left ) ||
|
|
|
|
( c.centerY >= innerQuad.bottom );
|
2017-10-17 17:29:02 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case BottomRight:
|
|
|
|
{
|
|
|
|
c.radiusInnerX = c.radiusX - borderRight;
|
|
|
|
c.radiusInnerY = c.radiusY - borderBottom;
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
c.isCropped = ( c.centerX >= innerQuad.right ) ||
|
|
|
|
( c.centerY >= innerQuad.bottom );
|
2017-10-17 17:29:02 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
isTotallyCropped =
|
|
|
|
corner[ TopLeft ].isCropped &&
|
|
|
|
corner[ TopRight ].isCropped &&
|
|
|
|
corner[ BottomRight ].isCropped &&
|
|
|
|
corner[ BottomLeft ].isCropped;
|
|
|
|
|
|
|
|
// number of steps for iterating over the corners
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
isBorderRegular =
|
|
|
|
( borderLeft == borderTop ) &&
|
|
|
|
( borderTop == borderRight ) &&
|
|
|
|
( borderRight == borderBottom );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
|
2023-01-11 12:51:16 +01:00
|
|
|
void QskRoundedRectRenderer::renderBorderGeometry(
|
2017-10-17 17:29:02 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
const int stepCount = metrics.corner[ 0 ].stepCount;
|
2017-10-17 17:29:02 +02:00
|
|
|
const int lineCount = 4 * ( stepCount + 1 ) + 1;
|
|
|
|
|
|
|
|
const auto line = allocateLines< Line >( geometry, lineCount );
|
|
|
|
qskRenderBorderLines( metrics, Qt::Vertical, line, BorderMapNone() );
|
|
|
|
}
|
|
|
|
|
2023-01-11 12:51:16 +01:00
|
|
|
void QskRoundedRectRenderer::renderFillGeometry(
|
2017-10-17 17:29:02 +02:00
|
|
|
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 ) )
|
|
|
|
{
|
2022-12-05 11:39:09 +01:00
|
|
|
geometry.allocate( 0 );
|
2017-10-17 17:29:02 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( metrics.isTotallyCropped )
|
|
|
|
{
|
|
|
|
// degenerated to a rectangle
|
|
|
|
|
2022-12-05 11:39:09 +01:00
|
|
|
geometry.allocate( 4 );
|
|
|
|
|
|
|
|
const auto& quad = metrics.innerQuad;
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2022-12-05 11:39:09 +01:00
|
|
|
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 );
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-01-11 12:51:16 +01:00
|
|
|
#if 0
|
|
|
|
if ( metrics.isBorderRegular && metrics.isRadiusRegular )
|
|
|
|
{
|
|
|
|
int lineCount += metrics.corner[ TopLeft ].stepCount +
|
|
|
|
metrics.corner[ BottomLeft ].stepCount;
|
|
|
|
|
|
|
|
if ( metrics.centerQuad.top >= metrics.centerQuad.bottom )
|
|
|
|
lineCount--;
|
|
|
|
|
|
|
|
geometry.allocate( 2 * lineCount );
|
|
|
|
|
|
|
|
// TODO ...
|
|
|
|
qskRenderLines( metrics, Qt::Vertical, ... );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2022-12-05 11:39:09 +01:00
|
|
|
/*
|
|
|
|
Unfortunately QSGGeometry::DrawTriangleFan is no longer supported with
|
|
|
|
Qt6 and we have to go with DrawTriangleStrip, duplicating the center with
|
2023-01-11 12:51:16 +01:00
|
|
|
each vertex.
|
2022-12-05 11:39:09 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
const auto numPoints =
|
|
|
|
metrics.corner[0].stepCount + metrics.corner[1].stepCount
|
|
|
|
+ metrics.corner[2].stepCount + metrics.corner[3].stepCount + 4;
|
|
|
|
|
|
|
|
/*
|
|
|
|
points: center point + interpolated corner points
|
|
|
|
indexes: lines between the center and each point, where
|
|
|
|
the first line needs to be appended to close the filling
|
|
|
|
*/
|
|
|
|
|
|
|
|
geometry.allocate( 1 + numPoints, 2 * ( numPoints + 1 ) );
|
|
|
|
|
|
|
|
Q_ASSERT( geometry.sizeOfIndex() == 2 );
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2022-12-05 11:39:09 +01:00
|
|
|
auto p = geometry.vertexDataAsPoint2D();
|
|
|
|
auto idx = geometry.indexDataAsUShort();
|
|
|
|
|
|
|
|
int i = 0;
|
2022-12-28 19:39:08 +01:00
|
|
|
|
2022-12-05 11:39:09 +01:00
|
|
|
p[i++].set( rect.x() + 0.5 * rect.width(), rect.y() + 0.5 * rect.height() );
|
|
|
|
|
2022-12-28 19:39:08 +01:00
|
|
|
BorderValuesMulti v( metrics );
|
2022-07-18 16:27:22 +02:00
|
|
|
|
|
|
|
{
|
2022-12-05 11:39:09 +01:00
|
|
|
constexpr auto id = TopLeft;
|
|
|
|
const auto& c = metrics.corner[ id ];
|
|
|
|
|
|
|
|
for ( ArcIterator it( c.stepCount, false ); !it.isDone(); ++it )
|
|
|
|
{
|
|
|
|
*idx++ = 0;
|
|
|
|
*idx++ = i;
|
|
|
|
|
|
|
|
v.setAngle( it.cos(), it.sin() );
|
|
|
|
p[i++].set( c.centerX - v.dx1( id ), c.centerY - v.dy1( id ) );
|
|
|
|
}
|
2022-07-18 16:27:22 +02:00
|
|
|
}
|
|
|
|
{
|
2022-12-05 11:39:09 +01:00
|
|
|
constexpr auto id = BottomLeft;
|
|
|
|
const auto& c = metrics.corner[ id ];
|
|
|
|
|
|
|
|
for ( ArcIterator it( c.stepCount, true ); !it.isDone(); ++it )
|
|
|
|
{
|
|
|
|
*idx++ = 0;
|
|
|
|
*idx++ = i;
|
|
|
|
|
|
|
|
v.setAngle( it.cos(), it.sin() );
|
|
|
|
p[i++].set( c.centerX - v.dx1( id ), c.centerY + v.dy1( id ) );
|
|
|
|
}
|
2022-07-18 16:27:22 +02:00
|
|
|
}
|
2022-12-05 11:39:09 +01:00
|
|
|
{
|
|
|
|
constexpr auto id = BottomRight;
|
|
|
|
const auto& c = metrics.corner[ id ];
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2022-12-05 11:39:09 +01:00
|
|
|
for ( ArcIterator it( c.stepCount, false ); !it.isDone(); ++it )
|
|
|
|
{
|
|
|
|
*idx++ = 0;
|
|
|
|
*idx++ = i;
|
|
|
|
|
|
|
|
v.setAngle( it.cos(), it.sin() );
|
|
|
|
p[i++].set( c.centerX + v.dx1( id ), c.centerY + v.dy1( id ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
constexpr auto id = TopRight;
|
|
|
|
const auto& c = metrics.corner[ id ];
|
|
|
|
|
|
|
|
for ( ArcIterator it( c.stepCount, true ); !it.isDone(); ++it )
|
|
|
|
{
|
|
|
|
*idx++ = 0;
|
|
|
|
*idx++ = i;
|
|
|
|
|
|
|
|
v.setAngle( it.cos(), it.sin() );
|
|
|
|
p[i++].set( c.centerX + v.dx1( id ), c.centerY - v.dy1( id ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*idx++ = 0;
|
|
|
|
*idx++ = 1;
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
|
2023-01-10 15:36:43 +01:00
|
|
|
void QskRoundedRectRenderer::renderRect( const QRectF& rect,
|
2017-10-17 17:29:02 +02:00
|
|
|
const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& border,
|
2022-11-13 17:22:09 +01:00
|
|
|
const QskBoxBorderColors& borderColors, const QskGradient& gradient,
|
2017-10-17 17:29:02 +02:00
|
|
|
QSGGeometry& geometry )
|
|
|
|
{
|
|
|
|
const Metrics metrics( rect, shape, border );
|
2023-01-05 13:06:32 +01:00
|
|
|
const auto isTilted = gradient.linearDirection().isTilted();
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
int fillLineCount = 0;
|
|
|
|
|
|
|
|
if ( !metrics.innerQuad.isEmpty() && gradient.isVisible() )
|
|
|
|
{
|
|
|
|
if ( metrics.isTotallyCropped )
|
|
|
|
{
|
|
|
|
// degenerated to a rectangle
|
|
|
|
|
2022-10-24 17:29:39 +02:00
|
|
|
fillLineCount = gradient.stepCount() + 1;
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
#if 1
|
2023-01-05 13:06:32 +01:00
|
|
|
if ( isTilted )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
|
|
|
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 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
const int stepCount = metrics.corner[ 0 ].stepCount;
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
int borderLineCount = 0;
|
2022-02-04 16:11:11 +01:00
|
|
|
|
2017-10-17 17:29:02 +02:00
|
|
|
if ( borderColors.isVisible() && metrics.innerQuad != metrics.outerQuad )
|
2022-02-04 16:11:11 +01:00
|
|
|
{
|
2017-10-17 17:29:02 +02:00
|
|
|
borderLineCount = 4 * ( stepCount + 1 ) + 1;
|
|
|
|
|
2022-02-04 16:11:11 +01:00
|
|
|
const int additionalLines =
|
2022-04-16 16:01:40 +02:00
|
|
|
additionalGradientStops( borderColors.left() )
|
|
|
|
+ additionalGradientStops( borderColors.top() )
|
|
|
|
+ additionalGradientStops( borderColors.right() )
|
|
|
|
+ additionalGradientStops( borderColors.bottom() );
|
2022-02-04 16:11:11 +01:00
|
|
|
|
|
|
|
borderLineCount += additionalLines;
|
|
|
|
}
|
|
|
|
|
2017-10-17 17:29:02 +02:00
|
|
|
int lineCount = borderLineCount + fillLineCount;
|
|
|
|
|
|
|
|
bool extraLine = false;
|
|
|
|
if ( borderLineCount > 0 && fillLineCount > 0 )
|
|
|
|
{
|
2023-01-05 13:06:32 +01:00
|
|
|
if ( !gradient.isMonochrome() && isTilted )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
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++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-05 13:06:32 +01:00
|
|
|
auto lines = allocateLines< ColoredLine >( geometry, lineCount );
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
bool fillRandom = true;
|
|
|
|
if ( fillLineCount > 0 )
|
|
|
|
{
|
|
|
|
if ( metrics.isTotallyCropped )
|
|
|
|
{
|
|
|
|
fillRandom = false;
|
|
|
|
}
|
|
|
|
else if ( !gradient.isMonochrome() )
|
|
|
|
{
|
2023-01-05 13:06:32 +01:00
|
|
|
if ( gradient.stepCount() > 1 || isTilted )
|
2017-10-17 17:29:02 +02:00
|
|
|
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 )
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
qskRenderBoxRandom( metrics, borderColors,
|
2023-01-05 13:06:32 +01:00
|
|
|
gradient, lines, lines + fillLineCount );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( metrics.isTotallyCropped )
|
|
|
|
{
|
2023-01-05 13:06:32 +01:00
|
|
|
QskRectRenderer::renderFill0( metrics.innerQuad,
|
|
|
|
gradient, fillLineCount, lines );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
2023-01-05 13:06:32 +01:00
|
|
|
else if ( isTilted )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
2023-01-05 13:06:32 +01:00
|
|
|
renderDiagonalFill( metrics, gradient, fillLineCount, lines );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-01-05 13:06:32 +01:00
|
|
|
qskRenderFillOrdered( metrics, gradient, fillLineCount, lines );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
|
2023-01-05 13:06:32 +01:00
|
|
|
auto borderLines = lines + fillLineCount;
|
2017-10-17 17:29:02 +02:00
|
|
|
if ( extraLine )
|
|
|
|
borderLines++;
|
|
|
|
|
2021-10-19 06:44:17 +02:00
|
|
|
const auto orientation = qskQtOrientation( gradient );
|
2017-10-17 17:29:02 +02:00
|
|
|
qskRenderBorder( metrics, orientation, borderColors, borderLines );
|
|
|
|
|
|
|
|
if ( extraLine )
|
|
|
|
{
|
2023-01-05 13:06:32 +01:00
|
|
|
const auto l = lines + fillLineCount;
|
2018-08-03 08:15:28 +02:00
|
|
|
l[ 0 ].p1 = l[ -1 ].p2;
|
|
|
|
l[ 0 ].p2 = l[ 1 ].p1;
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ( fillLineCount > 0 )
|
|
|
|
{
|
|
|
|
if ( fillRandom )
|
|
|
|
{
|
2023-01-05 13:06:32 +01:00
|
|
|
qskRenderFillRandom( metrics, gradient, lines );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( metrics.isTotallyCropped )
|
|
|
|
{
|
2023-01-05 13:06:32 +01:00
|
|
|
QskRectRenderer::renderFill0( metrics.innerQuad,
|
|
|
|
gradient, fillLineCount, lines );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
2023-01-05 13:06:32 +01:00
|
|
|
else if ( isTilted )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
2023-01-05 13:06:32 +01:00
|
|
|
renderDiagonalFill( metrics, gradient, fillLineCount, lines );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-01-05 13:06:32 +01:00
|
|
|
qskRenderFillOrdered( metrics, gradient, fillLineCount, lines );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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
|
2023-01-05 13:06:32 +01:00
|
|
|
qskRenderBorder( metrics, Qt::Vertical, borderColors, lines );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
}
|