Name | |
---|---|
void | qskCreateFillOrdered(const QskBoxRenderer::Quad & rect, const QskGradient & gradient, ColoredLine * line) |
template <class ColorMap ,class Line > void |
qskCreateFillRandom(QskGradient::Orientation orientation, const QskBoxRenderer::Quad & r, const ColorMap & map, Line * line) |
template <class Line > void |
qskCreateBorder(const QskBoxRenderer::Quad & out, const QskBoxRenderer::Quad & in, QRgb rgb, Line * line) |
template <class Line > void |
qskCreateBorder(const QskBoxRenderer::Quad & out, const QskBoxRenderer::Quad & in, Color color, Line * line) |
template <class Line > void |
qskCreateBorder(const QskBoxRenderer::Quad & out, const QskBoxRenderer::Quad & in, const QskBoxBorderColors & colors, Line * line) |
static inline void qskCreateFillOrdered(
const QskBoxRenderer::Quad & rect,
const QskGradient & gradient,
ColoredLine * line
)
template <class ColorMap ,
class Line >
static inline void qskCreateFillRandom(
QskGradient::Orientation orientation,
const QskBoxRenderer::Quad & r,
const ColorMap & map,
Line * line
)
template <class Line >
static inline void qskCreateBorder(
const QskBoxRenderer::Quad & out,
const QskBoxRenderer::Quad & in,
QRgb rgb,
Line * line
)
template <class Line >
static inline void qskCreateBorder(
const QskBoxRenderer::Quad & out,
const QskBoxRenderer::Quad & in,
Color color,
Line * line
)
template <class Line >
static inline void qskCreateBorder(
const QskBoxRenderer::Quad & out,
const QskBoxRenderer::Quad & in,
const QskBoxBorderColors & colors,
Line * line
)
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#include "QskBoxBorderColors.h"
#include "QskBoxBorderMetrics.h"
#include "QskBoxRenderer.h"
#include "QskBoxRendererColorMap.h"
#include "QskFunctions.h"
#include "QskGradient.h"
#include "QskVertex.h"
using namespace QskVertex;
namespace
{
class VRectIterator
{
public:
inline VRectIterator( const QskBoxRenderer::Quad& rect )
: m_rect( rect )
, m_value( rect.top )
{
}
template< class ColorIterator >
inline void setGradientLine( const ColorIterator& it, ColoredLine* line )
{
line->setHLine( m_rect.left, m_rect.right, it.value(), it.color() );
}
template< class ColorIterator >
inline void setContourLine( const ColorIterator& it, ColoredLine* line )
{
line->setHLine( m_rect.left, m_rect.right, m_value, it.colorAt( m_value ) );
}
inline qreal value() const
{
return m_value;
}
inline bool advance()
{
if ( m_value == m_rect.top )
{
m_value = m_rect.bottom;
return true;
}
return false;
}
private:
const QskBoxRenderer::Quad& m_rect;
qreal m_value;
};
class HRectIterator
{
public:
inline HRectIterator( const QskBoxRenderer::Quad& rect )
: m_rect( rect )
, m_value( rect.left )
{
}
template< class ColorIterator >
inline void setGradientLine( const ColorIterator& it, ColoredLine* line )
{
line->setVLine( it.value(), m_rect.top, m_rect.bottom, it.color() );
}
template< class ColorIterator >
inline void setContourLine( const ColorIterator& it, ColoredLine* line )
{
line->setVLine( m_value, m_rect.top, m_rect.bottom, it.colorAt( m_value ) );
}
inline qreal value() const
{
return m_value;
}
inline bool advance()
{
if ( m_value == m_rect.left )
{
m_value = m_rect.right;
return true;
}
return false;
}
private:
const QskBoxRenderer::Quad& m_rect;
qreal m_value;
};
class DSquareIterator
{
public:
inline DSquareIterator( const QskBoxRenderer::Quad& rect )
: m_rect( rect )
, m_step( 0 )
{
Q_ASSERT( rect.width == rect.height );
}
template< class ColorIterator >
inline void setGradientLine( const ColorIterator& it, ColoredLine* line )
{
const auto v = it.value();
if ( v == 0.5 )
{
// this line is also a contour line, so we can skip it
return;
}
else if ( v < 0.5 )
{
const qreal dt = m_rect.width * 2 * v;
line->setLine( m_rect.left, m_rect.top + dt,
m_rect.left + dt, m_rect.top, it.color() );
}
else
{
const qreal dt = m_rect.width * 2 * ( v - 0.5 );
line->setLine( m_rect.left + dt, m_rect.bottom,
m_rect.right, m_rect.top + dt, it.color() );
}
}
template< class ColorIterator >
inline void setContourLine( const ColorIterator& it, ColoredLine* line )
{
const auto color = it.colorAt( value() );
const auto& r = m_rect;
switch ( m_step )
{
case 0:
{
line->setLine( r.left, r.top, r.left, r.top, color );
break;
}
case 1:
{
line->setLine( r.left, r.bottom, r.right, r.top, color );
break;
}
case 2:
{
line->setLine( r.right, r.bottom, r.right, r.bottom, color );
break;
}
}
}
inline qreal value() const
{
return m_step * 0.5;
}
inline bool advance()
{
return ++m_step <= 2;
}
private:
const QskBoxRenderer::Quad& m_rect;
int m_step;
};
class DRectIterator
{
public:
inline DRectIterator( const QskBoxRenderer::Quad& rect )
: m_rect( rect )
, m_step( 0 )
{
const qreal w = rect.width;
const qreal h = rect.height;
Q_ASSERT( w != h );
const qreal w2 = w * w;
const qreal h2 = h * h;
m_fx = ( w2 + h2 ) / w;
m_fy = ( w2 + h2 ) / h;
m_lx = m_rect.top - h2 / w;
m_ly = m_rect.left - w2 / h;
m_valueTR = w2 / ( w2 + h2 );
m_valueBL = h2 / ( w2 + h2 );
}
template< class ColorIterator >
inline void setGradientLine( const ColorIterator& it, ColoredLine* line )
{
const auto v = it.value();
const auto color = it.color();
const auto& r = m_rect;
switch ( m_step )
{
case 1:
{
const qreal dx = v * m_fx;
const qreal dy = v * m_fy;
line->setLine( r.left, r.top + dy, r.left + dx, r.top, color );
break;
}
case 2:
{
if ( r.width > r.height )
{
const qreal dx = v * m_fx;
line->setLine( m_lx + dx, r.bottom, r.left + dx, r.top, color );
}
else
{
const qreal dy = v * m_fy;
line->setLine( r.left, r.top + dy, r.right, m_ly + dy, color );
}
break;
}
case 3:
{
const qreal dx = v * m_fx;
const qreal dy = v * m_fy;
line->setLine( m_lx + dx, r.bottom, r.right, m_ly + dy, color );
break;
}
}
}
template< class ColorIterator >
inline void setContourLine( const ColorIterator& it, ColoredLine* line )
{
const auto& r = m_rect;
switch ( m_step )
{
case 0:
{
line->setLine( r.left, r.top,
r.left, r.top, it.colorAt( 0.0 ) );
break;
}
case 1:
{
if ( r.width >= r.height )
{
const qreal dx = m_valueBL * m_fx;
line->setLine( r.left, r.bottom,
r.left + dx, r.top, it.colorAt( m_valueBL ) );
}
else
{
const qreal dy = m_valueTR * m_fy;
line->setLine( r.left, r.top + dy,
r.right, r.top, it.colorAt( m_valueTR ) );
}
break;
}
case 2:
{
if ( r.width >= r.height )
{
const qreal dx = m_valueTR * m_fx;
line->setLine( r.left + dx, r.bottom,
r.right, r.top, it.colorAt( m_valueTR ) );
}
else
{
const qreal dy = m_valueBL * m_fy;
line->setLine( r.left, r.bottom,
r.right, r.top + dy, it.colorAt( m_valueBL ) );
}
break;
}
case 3:
{
line->setLine( r.right, r.bottom,
r.right, r.bottom, it.colorAt( 1.0 ) );
break;
}
default:
{
Q_ASSERT( false );
}
}
}
inline qreal value() const
{
switch ( m_step )
{
case 0:
return 0.0;
case 1:
return std::min( m_valueBL, m_valueTR );
case 2:
return std::max( m_valueBL, m_valueTR );
default:
return 1.0;
}
}
inline bool advance()
{
return ++m_step <= 3;
}
private:
const QskBoxRenderer::Quad& m_rect;
qreal m_fx, m_fy;
qreal m_lx, m_ly;
qreal m_valueTR, m_valueBL;
int m_step;
};
}
static inline void qskCreateFillOrdered( const QskBoxRenderer::Quad& rect,
const QskGradient& gradient, ColoredLine* line )
{
switch ( gradient.orientation() )
{
case QskGradient::Horizontal:
{
HRectIterator it( rect );
line = QskVertex::fillOrdered( it, rect.left, rect.right, gradient, line );
break;
}
case QskGradient::Vertical:
{
VRectIterator it( rect );
line = QskVertex::fillOrdered( it, rect.top, rect.bottom, gradient, line );
break;
}
case QskGradient::Diagonal:
{
if ( rect.width == rect.height )
{
DSquareIterator it( rect );
line = QskVertex::fillOrdered( it, 0.0, 1.0, gradient, line );
}
else
{
DRectIterator it( rect );
line = QskVertex::fillOrdered( it, 0.0, 1.0, gradient, line );
}
break;
}
}
}
template< class ColorMap, class Line >
static inline void qskCreateFillRandom(
QskGradient::Orientation orientation,
const QskBoxRenderer::Quad& r, const ColorMap& map, Line* line )
{
if ( orientation == QskGradient::Vertical )
{
( line++ )->setLine( r.left, r.top, r.right, r.top, map.colorAt( 0.0 ) );
( line++ )->setLine( r.left, r.bottom, r.right, r.bottom, map.colorAt( 1.0 ) );
}
else
{
( line++ )->setLine( r.left, r.top, r.left, r.bottom, map.colorAt( 0.0 ) );
( line++ )->setLine( r.right, r.top, r.right, r.bottom, map.colorAt( 1.0 ) );
}
}
template< class Line >
static inline void qskCreateBorder(
const QskBoxRenderer::Quad& out, const QskBoxRenderer::Quad& in, QRgb rgb, Line* line )
{
qskCreateBorder( out, in, Color( rgb ), line );
}
template< class Line >
static inline void qskCreateBorder(
const QskBoxRenderer::Quad& out, const QskBoxRenderer::Quad& in, Color color, Line* line )
{
auto l = line;
( l++ )->setLine( in.right, in.bottom, out.right, out.bottom, color );
( l++ )->setLine( in.left, in.bottom, out.left, out.bottom, color );
( l++ )->setLine( in.left, in.top, out.left, out.top, color );
( l++ )->setLine( in.right, in.top, out.right, out.top, color );
*l = line[ 0 ];
}
template< class Line >
static inline void qskCreateBorder(
const QskBoxRenderer::Quad& out, const QskBoxRenderer::Quad& in,
const QskBoxBorderColors& colors, Line* line )
{
const Color colorLeft = colors.rgb( Qsk::Left );
const Color colorRight = colors.rgb( Qsk::Right );
const Color colorTop = colors.rgb( Qsk::Top );
const Color colorBottom = colors.rgb( Qsk::Bottom );
( line++ )->setLine( in.right, in.bottom, out.right, out.bottom, colorBottom );
( line++ )->setLine( in.left, in.bottom, out.left, out.bottom, colorBottom );
if ( colorLeft != colorBottom )
( line++ )->setLine( in.left, in.bottom, out.left, out.bottom, colorLeft );
( line++ )->setLine( in.left, in.top, out.left, out.top, colorLeft );
if ( colorTop != colorLeft )
( line++ )->setLine( in.left, in.top, out.left, out.top, colorTop );
( line++ )->setLine( in.right, in.top, out.right, out.top, colorTop );
if ( colorRight != colorTop )
( line++ )->setLine( in.right, in.top, out.right, out.top, colorRight );
( line++ )->setLine( in.right, in.bottom, out.right, out.bottom, colorRight );
}
void QskBoxRenderer::renderRectBorder(
const QRectF& rect, const QskBoxShapeMetrics& shape,
const QskBoxBorderMetrics& border, QSGGeometry& geometry )
{
Q_UNUSED( shape );
const Quad out = rect;
const Quad in = qskValidOrEmptyInnerRect( rect, border.widths() );
if ( out == in )
{
allocateLines< Line >( geometry, 0 );
return;
}
const auto line = allocateLines< Line >( geometry, 4 + 1 );
qskCreateBorder( out, in, Color(), line );
}
void QskBoxRenderer::renderRectFill(
const QRectF& rect, const QskBoxShapeMetrics& shape,
const QskBoxBorderMetrics& border, QSGGeometry& geometry )
{
Q_UNUSED( shape );
const Quad in = qskValidOrEmptyInnerRect( rect, border.widths() );
if ( in.isEmpty() )
{
allocateLines< Line >( geometry, 0 );
return;
}
const auto line = allocateLines< Line >( geometry, 2 );
qskCreateFillRandom( QskGradient::Vertical,
in, ColorMapSolid( Color() ), line );
}
void QskBoxRenderer::renderRect(
const QRectF& rect, const QskBoxShapeMetrics& shape,
const QskBoxBorderMetrics& border, const QskBoxBorderColors& borderColors,
const QskGradient& gradient, QSGGeometry& geometry )
{
Q_UNUSED( shape );
const Quad out = rect;
const Quad in = qskValidOrEmptyInnerRect( rect, border.widths() );
int fillLineCount = 0;
if ( !in.isEmpty() )
{
fillLineCount = gradient.stops().count();
if ( gradient.orientation() == QskGradient::Diagonal )
{
if ( in.width == in.height )
{
if ( !gradient.hasStopAt( 0.5 ) )
fillLineCount++;
}
else
{
// we might need extra lines for the corners
fillLineCount += 2;
}
}
}
int borderLineCount = 0;
if ( in != out )
{
const auto& bc = borderColors;
if ( bc.isVisible() )
{
// We can build a rectangular border from the 4 diagonal
// lines at the corners, but need an additional line
// for closing the border.
borderLineCount = 4 + 1;
if ( !bc.isMonochrome() )
{
// we might need extra lines to separate colors
// at the non closing corners
if ( bc.color( Qsk::Left ) != bc.color( Qsk::Bottom ) )
borderLineCount++;
if ( bc.color( Qsk::Top ) != bc.color( Qsk::Left ) )
borderLineCount++;
if ( bc.color( Qsk::Right ) != bc.color( Qsk::Top ) )
borderLineCount++;
}
}
}
auto line = allocateLines< ColoredLine >( geometry, borderLineCount + fillLineCount );
if ( fillLineCount > 0 )
{
const auto& gd = gradient;
if ( gd.isMonochrome() )
{
const ColorMapSolid colorMap( gd.startColor() );
qskCreateFillRandom( QskGradient::Vertical, in, colorMap, line );
}
else
{
bool fillRandom = gd.stops().count() <= 2;
if ( fillRandom )
{
/*
Not necessarily a requirement for being ordered,
but we didn't implement a random fill algo for
diagonal gradients yet.
*/
fillRandom = gd.orientation() != QskGradient::Diagonal;
}
if ( fillRandom )
{
const ColorMapGradient colorMap( gd.startColor(), gd.endColor() );
qskCreateFillRandom( gd.orientation(), in, colorMap, line );
}
else
{
qskCreateFillOrdered( in, gd, line );
}
}
}
if ( borderLineCount > 0 )
{
const auto& bc = borderColors;
auto fillLines = line + fillLineCount;
if ( bc.isMonochrome() )
qskCreateBorder( rect, in, bc.rgb( Qsk::Left ), fillLines );
else
qskCreateBorder( rect, in, bc, fillLines );
}
}
void QskBoxRenderer::renderRectFill( const QskBoxRenderer::Quad& rect,
const QskGradient& gradient, QskVertex::ColoredLine* line )
{
qskCreateFillOrdered( rect, gradient, line );
}
Updated on 28 July 2023 at 14:02:30 CEST