2024-09-11 10:24:22 +02:00
|
|
|
/******************************************************************************
|
|
|
|
* QSkinny - Copyright (C) The authors
|
|
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
#include "QskArcRenderNode.h"
|
|
|
|
#include "QskGradient.h"
|
|
|
|
#include "QskArcRenderer.h"
|
|
|
|
#include "QskArcMetrics.h"
|
|
|
|
#include "QskGradient.h"
|
|
|
|
#include "QskSGNode.h"
|
2024-09-24 10:14:26 +02:00
|
|
|
#include "QskRgbValue.h"
|
2024-09-11 10:24:22 +02:00
|
|
|
#include "QskFillNodePrivate.h"
|
|
|
|
|
2024-09-23 16:04:09 +02:00
|
|
|
static inline bool qskHasBorder( qreal width, const QColor& color )
|
|
|
|
{
|
2024-09-24 10:14:26 +02:00
|
|
|
return ( width > 0.0 ) && QskRgb::isVisible( color );
|
2024-09-23 16:04:09 +02:00
|
|
|
}
|
|
|
|
|
2024-09-11 10:24:22 +02:00
|
|
|
class QskArcRenderNodePrivate final : public QskFillNodePrivate
|
|
|
|
{
|
|
|
|
public:
|
2024-09-23 16:04:09 +02:00
|
|
|
inline void resetNode( QskArcRenderNode* node )
|
|
|
|
{
|
|
|
|
m_metricsHash = m_colorsHash = 0;
|
|
|
|
node->resetGeometry();
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool updateMetrics( const QRectF& rect,
|
|
|
|
const QskArcMetrics& metrics, bool radial, qreal borderWidth )
|
|
|
|
{
|
|
|
|
QskHashValue hash = 13000;
|
|
|
|
|
|
|
|
hash = qHashBits( &rect, sizeof( rect ), hash );
|
|
|
|
hash = metrics.hash( hash );
|
|
|
|
hash = qHash( radial, hash );
|
|
|
|
hash = qHash( borderWidth, hash );
|
|
|
|
|
|
|
|
return updateHash( m_metricsHash, hash );
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool updateColors( const QColor& borderColor, const QskGradient& gradient )
|
|
|
|
{
|
|
|
|
QskHashValue hash = 13000;
|
|
|
|
|
2024-09-24 10:14:26 +02:00
|
|
|
if ( QskRgb::isVisible( borderColor ) )
|
2024-09-23 16:04:09 +02:00
|
|
|
hash = qHashBits( &borderColor, sizeof( borderColor ), hash );
|
|
|
|
|
|
|
|
if ( gradient.isVisible() )
|
|
|
|
hash = gradient.hash( hash );
|
|
|
|
|
|
|
|
return updateHash( m_colorsHash, hash );
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
inline bool updateHash( QskHashValue& value, const QskHashValue newValue ) const
|
|
|
|
{
|
|
|
|
if ( newValue != value )
|
|
|
|
{
|
|
|
|
value = newValue;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2024-09-11 10:24:22 +02:00
|
|
|
|
2024-09-23 16:04:09 +02:00
|
|
|
public:
|
|
|
|
QskHashValue m_metricsHash = 0;
|
|
|
|
QskHashValue m_colorsHash = 0;
|
2024-09-11 10:24:22 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
QskArcRenderNode::QskArcRenderNode()
|
|
|
|
: QskFillNode( *new QskArcRenderNodePrivate )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QskArcRenderNode::~QskArcRenderNode()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2024-09-23 16:04:09 +02:00
|
|
|
void QskArcRenderNode::updateFilling( const QRectF& rect,
|
2024-09-11 10:24:22 +02:00
|
|
|
const QskArcMetrics& metrics, const QskGradient& gradient )
|
|
|
|
{
|
2024-09-23 16:04:09 +02:00
|
|
|
updateFilling( rect, metrics, false, 0.0, gradient );
|
2024-09-11 10:24:22 +02:00
|
|
|
}
|
|
|
|
|
2024-09-23 16:04:09 +02:00
|
|
|
void QskArcRenderNode::updateFilling( const QRectF& rect,
|
|
|
|
const QskArcMetrics& arcMetrics, bool radial,
|
|
|
|
qreal borderWidth, const QskGradient& gradient )
|
2024-09-11 10:24:22 +02:00
|
|
|
{
|
|
|
|
Q_D( QskArcRenderNode );
|
|
|
|
|
2024-09-23 16:04:09 +02:00
|
|
|
if ( rect.isEmpty() || arcMetrics.isNull() || !gradient.isVisible() )
|
|
|
|
{
|
|
|
|
d->resetNode( this );
|
|
|
|
return;
|
|
|
|
}
|
2024-09-11 10:24:22 +02:00
|
|
|
|
2024-09-23 16:04:09 +02:00
|
|
|
const auto metrics = arcMetrics.toAbsolute( rect.size() );
|
2024-09-11 10:24:22 +02:00
|
|
|
|
2024-09-23 16:04:09 +02:00
|
|
|
if ( borderWidth >= 0.5 * metrics.thickness() )
|
2024-09-11 10:24:22 +02:00
|
|
|
{
|
2024-09-23 16:04:09 +02:00
|
|
|
d->resetNode( this );
|
2024-09-11 10:24:22 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-09-24 12:20:03 +02:00
|
|
|
bool coloredGeometry = hasHint( PreferColoredGeometry );
|
|
|
|
if ( coloredGeometry )
|
|
|
|
{
|
|
|
|
// not all gradients are supported by the renderer
|
|
|
|
coloredGeometry = QskArcRenderer::isGradientSupported( rect, metrics, gradient );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// QskGradient::Stops is specific for QskArcRenderer
|
|
|
|
coloredGeometry = ( gradient.type() == QskGradient::Stops );
|
|
|
|
}
|
2024-09-11 10:24:22 +02:00
|
|
|
|
2024-09-23 16:04:09 +02:00
|
|
|
bool dirtyGeometry = d->updateMetrics( rect, metrics, radial, borderWidth );
|
|
|
|
bool dirtyMaterial = d->updateColors( QColor(), gradient );
|
2024-09-11 10:24:22 +02:00
|
|
|
|
2024-09-23 16:04:09 +02:00
|
|
|
if ( coloredGeometry != isGeometryColored() )
|
|
|
|
dirtyGeometry = dirtyMaterial = true;
|
2024-09-11 10:24:22 +02:00
|
|
|
|
2024-09-23 16:04:09 +02:00
|
|
|
if ( dirtyGeometry || dirtyMaterial )
|
|
|
|
{
|
|
|
|
if ( coloredGeometry )
|
|
|
|
{
|
|
|
|
setColoring( QskFillNode::Polychrome );
|
2024-09-11 10:24:22 +02:00
|
|
|
|
2024-09-23 16:04:09 +02:00
|
|
|
QskArcRenderer::setColoredFillLines( rect, metrics, radial,
|
|
|
|
borderWidth, gradient, *geometry() );
|
2024-09-11 10:24:22 +02:00
|
|
|
|
2024-09-23 16:04:09 +02:00
|
|
|
markDirty( QSGNode::DirtyGeometry );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
setColoring( rect, gradient );
|
2024-09-11 10:24:22 +02:00
|
|
|
|
2024-09-23 16:04:09 +02:00
|
|
|
if ( dirtyGeometry )
|
|
|
|
{
|
|
|
|
QskArcRenderer::setFillLines( rect, metrics,
|
|
|
|
radial, borderWidth, *geometry() );
|
|
|
|
|
|
|
|
markDirty( QSGNode::DirtyGeometry );
|
|
|
|
}
|
|
|
|
}
|
2024-09-11 10:24:22 +02:00
|
|
|
}
|
2024-09-23 16:04:09 +02:00
|
|
|
}
|
2024-09-11 10:24:22 +02:00
|
|
|
|
2024-09-23 16:04:09 +02:00
|
|
|
void QskArcRenderNode::updateBorder( const QRectF& rect,
|
|
|
|
const QskArcMetrics& arcMetrics, bool radial,
|
|
|
|
qreal borderWidth, const QColor& borderColor )
|
|
|
|
{
|
|
|
|
Q_D( QskArcRenderNode );
|
2024-09-11 10:24:22 +02:00
|
|
|
|
2024-09-23 16:04:09 +02:00
|
|
|
if ( rect.isEmpty() || arcMetrics.isNull()
|
|
|
|
|| !qskHasBorder( borderWidth, borderColor ) )
|
2024-09-11 10:24:22 +02:00
|
|
|
{
|
2024-09-23 16:04:09 +02:00
|
|
|
d->resetNode( this );
|
|
|
|
return;
|
2024-09-11 10:24:22 +02:00
|
|
|
}
|
2024-09-23 16:04:09 +02:00
|
|
|
|
|
|
|
const bool coloredGeometry = hasHint( PreferColoredGeometry );
|
|
|
|
|
|
|
|
bool dirtyGeometry = d->updateMetrics( rect, arcMetrics, radial, borderWidth );
|
|
|
|
bool dirtyMaterial = d->updateColors( borderColor, QskGradient() );
|
|
|
|
|
|
|
|
if ( coloredGeometry != isGeometryColored() )
|
|
|
|
dirtyGeometry = dirtyMaterial = true;
|
|
|
|
|
|
|
|
if ( dirtyGeometry || dirtyMaterial )
|
2024-09-11 10:24:22 +02:00
|
|
|
{
|
2024-09-23 16:04:09 +02:00
|
|
|
const auto metrics = arcMetrics.toAbsolute( rect.size() );
|
|
|
|
borderWidth = qMin( borderWidth, 0.5 * metrics.thickness() );
|
|
|
|
|
|
|
|
if ( coloredGeometry )
|
2024-09-11 10:24:22 +02:00
|
|
|
{
|
2024-09-23 16:04:09 +02:00
|
|
|
setColoring( QskFillNode::Polychrome );
|
|
|
|
|
|
|
|
QskArcRenderer::setColoredBorderLines( rect, metrics, radial,
|
|
|
|
borderWidth, borderColor, *geometry() );
|
2024-09-11 10:24:22 +02:00
|
|
|
|
2024-09-23 16:04:09 +02:00
|
|
|
markDirty( QSGNode::DirtyGeometry );
|
2024-09-11 10:24:22 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
setColoring( borderColor );
|
|
|
|
|
2024-09-23 16:04:09 +02:00
|
|
|
if ( dirtyGeometry )
|
|
|
|
{
|
|
|
|
QskArcRenderer::setBorderLines( rect, metrics, radial,
|
|
|
|
borderWidth, *geometry() );
|
|
|
|
|
|
|
|
markDirty( QSGNode::DirtyGeometry );
|
|
|
|
}
|
2024-09-11 10:24:22 +02:00
|
|
|
}
|
|
|
|
}
|
2024-09-23 16:04:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void QskArcRenderNode::updateArc(
|
|
|
|
const QRectF& rect, const QskArcMetrics& arcMetrics, bool radial,
|
|
|
|
qreal borderWidth, const QColor& borderColor, const QskGradient& gradient )
|
|
|
|
{
|
|
|
|
Q_D( QskArcRenderNode );
|
|
|
|
|
|
|
|
const auto metrics = arcMetrics.toAbsolute( rect.size() );
|
|
|
|
if ( rect.isEmpty() || metrics.isNull() )
|
|
|
|
{
|
|
|
|
d->resetNode( this );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto borderMax = 0.5 * metrics.thickness();
|
|
|
|
|
|
|
|
const bool hasFill = gradient.isVisible() && ( borderWidth < borderMax );
|
|
|
|
const bool hasBorder = qskHasBorder( borderWidth, borderColor );
|
|
|
|
|
|
|
|
if ( hasBorder && hasFill )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
When having a monochrome gradient that has the same color as the
|
|
|
|
border we might be able to use QskFillNode::Monochrome. For the
|
|
|
|
moment this is not implemented
|
|
|
|
*/
|
|
|
|
borderWidth = qMin( borderWidth, borderMax );
|
|
|
|
|
|
|
|
const bool isDirty = d->updateMetrics( rect, arcMetrics, radial, borderWidth )
|
|
|
|
|| d->updateColors( borderColor, gradient ) || !isGeometryColored();
|
|
|
|
|
|
|
|
if ( isDirty )
|
|
|
|
{
|
|
|
|
setColoring( QskFillNode::Polychrome );
|
2024-09-11 10:24:22 +02:00
|
|
|
|
2024-09-23 16:04:09 +02:00
|
|
|
QskArcRenderer::setColoredBorderAndFillLines( rect, metrics, radial,
|
|
|
|
borderWidth, borderColor, gradient, *geometry() );
|
2024-09-11 10:24:22 +02:00
|
|
|
|
2024-09-23 16:04:09 +02:00
|
|
|
markDirty( QSGNode::DirtyGeometry );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ( hasBorder )
|
|
|
|
{
|
|
|
|
updateBorder( rect, arcMetrics, radial, borderWidth, borderColor );
|
|
|
|
}
|
|
|
|
else if ( hasFill )
|
|
|
|
{
|
|
|
|
updateFilling( rect, arcMetrics, radial, borderWidth, gradient );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
d->resetNode( this );
|
|
|
|
return;
|
|
|
|
}
|
2024-09-11 10:24:22 +02:00
|
|
|
}
|