qskinny/src/nodes/QskBoxNode.cpp

287 lines
7.2 KiB
C++
Raw Normal View History

2017-07-21 18:21:34 +02:00
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#include "QskBoxNode.h"
2018-08-03 08:15:28 +02:00
#include "QskBoxBorderColors.h"
#include "QskBoxBorderMetrics.h"
#include "QskBoxRenderer.h"
2017-10-20 13:09:30 +02:00
#include "QskBoxShapeMetrics.h"
#include "QskGradient.h"
#include "QskGradientDirection.h"
2017-10-20 13:09:30 +02:00
2018-07-19 14:10:48 +02:00
#include <qglobalstatic.h>
2018-08-03 08:15:28 +02:00
#include <qsgflatcolormaterial.h>
#include <qsgvertexcolormaterial.h>
2017-07-21 18:21:34 +02:00
QSK_QT_PRIVATE_BEGIN
#include <private/qsgnode_p.h>
QSK_QT_PRIVATE_END
2017-10-30 14:38:30 +01:00
Q_GLOBAL_STATIC( QSGVertexColorMaterial, qskMaterialVertex )
static inline QskHashValue qskMetricsHash(
2018-08-03 08:15:28 +02:00
const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics )
{
QskHashValue hash = 13000;
2017-10-20 13:09:30 +02:00
hash = shape.hash( hash );
return borderMetrics.hash( hash );
}
2017-07-21 18:21:34 +02:00
static inline QskHashValue qskColorsHash(
2018-08-03 08:15:28 +02:00
const QskBoxBorderColors& borderColors, const QskGradient& fillGradient )
{
QskHashValue hash = 13000;
2017-10-20 13:09:30 +02:00
hash = borderColors.hash( hash );
return fillGradient.hash( hash );
2017-07-21 18:21:34 +02:00
}
2022-10-31 14:42:08 +01:00
#if 1
static inline QskGradient qskEffectiveGradient( const QskGradient& gradient )
2022-10-31 14:42:08 +01:00
{
QskGradient g;
2022-10-31 14:42:08 +01:00
if ( gradient.isVisible() )
{
if ( gradient.isMonochrome() )
{
g.setLinearDirection( Qt::Vertical );
2022-10-31 14:42:08 +01:00
g.setStops( gradient.startColor() );
}
else
{
2022-10-31 14:42:08 +01:00
switch( gradient.type() )
{
case QskGradient::Linear:
{
auto dir = gradient.linearDirection();
2022-10-31 14:42:08 +01:00
if ( dir.isTilted() )
2022-10-31 14:42:08 +01:00
{
dir.setStart( 0.0, 0.0 );
dir.setStop( 1.0, 1.0 );
2022-10-31 14:42:08 +01:00
}
g.setLinearDirection( dir );
2022-10-31 14:42:08 +01:00
break;
}
case QskGradient::Radial:
case QskGradient::Conic:
{
qWarning() << "QskBoxNode does not support radial/conic gradients";
g.setLinearDirection( Qt::Vertical );
2022-10-31 14:42:08 +01:00
break;
}
case QskGradient::Stops:
{
g.setLinearDirection( Qt::Vertical );
2022-10-31 14:42:08 +01:00
break;
}
}
g.setStops( gradient.stops() );
2022-10-31 14:42:08 +01:00
}
}
return g;
}
#endif
class QskBoxNodePrivate final : public QSGGeometryNodePrivate
{
public:
QskBoxNodePrivate()
: geometry( QSGGeometry::defaultAttributes_ColoredPoint2D(), 0 )
{
}
QskHashValue metricsHash = 0;
QskHashValue colorsHash = 0;
QRectF rect;
QSGGeometry geometry;
};
2018-08-03 08:15:28 +02:00
QskBoxNode::QskBoxNode()
: QSGGeometryNode( *new QskBoxNodePrivate )
2017-07-21 18:21:34 +02:00
{
Q_D( QskBoxNode );
2017-10-30 14:38:30 +01:00
setMaterial( qskMaterialVertex );
setGeometry( &d->geometry );
2017-07-21 18:21:34 +02:00
}
QskBoxNode::~QskBoxNode()
{
2017-10-30 14:38:30 +01:00
if ( material() != qskMaterialVertex )
delete material();
2017-07-21 18:21:34 +02:00
}
2017-10-20 13:09:30 +02:00
void QskBoxNode::setBoxData( const QRectF& rect, const QskGradient& fillGradient )
{
setBoxData( rect, QskBoxShapeMetrics(), QskBoxBorderMetrics(),
QskBoxBorderColors(), fillGradient );
}
2019-01-04 13:42:16 +01:00
void QskBoxNode::setBoxData( const QRectF& rect,
2017-10-20 13:09:30 +02:00
const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics,
const QskBoxBorderColors& borderColors, const QskGradient& gradient )
2017-07-21 18:21:34 +02:00
{
Q_D( QskBoxNode );
2022-10-31 14:42:08 +01:00
/*
QskBoxRenderer supports certain linear gradients only.
For everything else we would have to use a QskGradientMaterial instead.
As a temporary solution we simply "convert" gradient into a
simple QskLinearGradient so that at least something is happening.
TODO ...
*/
#if 1
2022-10-31 14:42:08 +01:00
const auto fillGradient = qskEffectiveGradient( gradient );
#endif
const auto metricsHash = qskMetricsHash( shape, borderMetrics );
const auto colorsHash = qskColorsHash( borderColors, fillGradient );
2017-07-21 18:21:34 +02:00
if ( ( metricsHash == d->metricsHash ) &&
( colorsHash == d->colorsHash ) && ( rect == d->rect ) )
2017-07-21 18:21:34 +02:00
{
return;
}
d->metricsHash = metricsHash;
d->colorsHash = colorsHash;
d->rect = rect;
markDirty( QSGNode::DirtyMaterial );
markDirty( QSGNode::DirtyGeometry );
if ( rect.isEmpty() )
{
d->geometry.allocate( 0 );
return;
2017-07-21 18:21:34 +02:00
}
bool hasFill = fillGradient.isVisible();
bool hasBorder = !borderMetrics.isNull();
if ( hasBorder )
2017-07-21 18:21:34 +02:00
{
/*
Wrong as the border width should have an
effect - even if not being visible. TODO ...
*/
2017-07-21 18:21:34 +02:00
hasBorder = borderColors.isVisible();
}
2017-07-21 18:21:34 +02:00
if ( !hasBorder && !hasFill )
{
d->geometry.allocate( 0 );
return;
2017-07-21 18:21:34 +02:00
}
2017-10-20 13:09:30 +02:00
const bool isFillMonochrome = hasFill ? fillGradient.isMonochrome() : true;
const bool isBorderMonochrome = hasBorder ? borderColors.isMonochrome() : true;
if ( hasFill && hasBorder )
2017-07-21 18:21:34 +02:00
{
if ( isFillMonochrome && isBorderMonochrome )
{
2022-04-16 16:01:40 +02:00
if ( borderColors.left().startColor() == fillGradient.startColor() )
{
// we can draw border and background in one
hasBorder = false;
}
}
}
#if 0
/*
Always using the same material result in a better batching
but wastes some memory. when we have a solid color.
Maybe its worth to introduce a flag to control the behaviour,
but for the moment we go with performance.
*/
2017-07-21 18:21:34 +02:00
bool maybeFlat = true;
if ( maybeFlat )
{
2018-08-03 08:15:28 +02:00
if ( ( hasFill && hasBorder ) ||
( hasFill && !isFillMonochrome ) ||
( hasBorder && !isBorderMonochrome ) )
{
maybeFlat = false;
}
}
#else
bool maybeFlat = false;
#endif
2017-07-21 18:21:34 +02:00
QskBoxRenderer renderer;
2017-07-21 18:21:34 +02:00
if ( !maybeFlat )
{
setMonochrome( false );
2017-07-21 18:21:34 +02:00
renderer.renderBox( d->rect, shape, borderMetrics,
2017-10-20 13:09:30 +02:00
borderColors, fillGradient, *geometry() );
}
else
{
// all is done with one color
setMonochrome( true );
2018-08-03 08:15:28 +02:00
auto* flatMaterial = static_cast< QSGFlatColorMaterial* >( material() );
if ( hasFill )
{
2017-10-20 13:09:30 +02:00
flatMaterial->setColor( fillGradient.startColor() );
renderer.renderFill( d->rect, shape, QskBoxBorderMetrics(), *geometry() );
}
else
{
2022-04-16 16:01:40 +02:00
flatMaterial->setColor( borderColors.left().startColor().rgba() );
renderer.renderBorder( d->rect, shape, borderMetrics, *geometry() );
}
2017-07-21 18:21:34 +02:00
}
}
void QskBoxNode::setMonochrome( bool on )
2017-07-21 18:21:34 +02:00
{
const auto material = this->material();
2017-10-30 14:38:30 +01:00
if ( on == ( material != qskMaterialVertex ) )
return;
Q_D( QskBoxNode );
d->geometry.allocate( 0 );
if ( on )
{
setMaterial( new QSGFlatColorMaterial() );
const QSGGeometry g( QSGGeometry::defaultAttributes_Point2D(), 0 );
memcpy( ( void* ) &d->geometry, ( void* ) &g, sizeof( QSGGeometry ) );
}
else
{
2017-10-30 14:38:30 +01:00
setMaterial( qskMaterialVertex );
delete material;
const QSGGeometry g( QSGGeometry::defaultAttributes_ColoredPoint2D(), 0 );
memcpy( ( void* ) &d->geometry, ( void* ) &g, sizeof( QSGGeometry ) );
}
2017-07-21 18:21:34 +02:00
}