Finally all types of gradients can be set to boxes.
For radial/conic and tilted linear gradients shape/border metrics will be translated into a QPainterPath to be fed into the triangulation. However the borders remain being done by QskBoxRenderer.
This commit is contained in:
parent
38cbefac78
commit
7abd90b2dd
183
src/nodes/QskBoxFillNode.cpp
Normal file
183
src/nodes/QskBoxFillNode.cpp
Normal file
@ -0,0 +1,183 @@
|
||||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
||||
* This file may be used under the terms of the QSkinny License, Version 1.0
|
||||
*****************************************************************************/
|
||||
|
||||
#include "QskBoxFillNode.h"
|
||||
#include "QskGradientMaterial.h"
|
||||
#include "QskGradient.h"
|
||||
#include "QskGradientDirection.h"
|
||||
#include "QskBoxShapeMetrics.h"
|
||||
#include "QskBoxBorderMetrics.h"
|
||||
#include "QskBoxRenderer.h"
|
||||
|
||||
#include <qsgflatcolormaterial.h>
|
||||
#include <qpainterpath.h>
|
||||
|
||||
QSK_QT_PRIVATE_BEGIN
|
||||
#include <private/qsgnode_p.h>
|
||||
#include <private/qvectorpath_p.h>
|
||||
#include <private/qtriangulator_p.h>
|
||||
QSK_QT_PRIVATE_END
|
||||
|
||||
static inline QskHashValue qskMetricsHash(
|
||||
const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics )
|
||||
{
|
||||
QskHashValue hash = 13000;
|
||||
|
||||
hash = shape.hash( hash );
|
||||
return borderMetrics.hash( hash );
|
||||
}
|
||||
|
||||
static inline QskGradient qskEffectiveGradient( const QskGradient& gradient )
|
||||
{
|
||||
if ( gradient.type() == QskGradient::Stops || gradient.isMonochrome() )
|
||||
{
|
||||
// the shader for linear gradients is the fastest
|
||||
QskGradient g;
|
||||
g.setLinearDirection( Qt::Vertical );
|
||||
g.setStops( gradient.stops() );
|
||||
|
||||
return g;
|
||||
}
|
||||
|
||||
return gradient;
|
||||
}
|
||||
|
||||
static void qskUpdateGeometry( const QPainterPath& path, QSGGeometry& geometry )
|
||||
{
|
||||
const auto ts = qTriangulate( path, QTransform(), 1, false );
|
||||
|
||||
/*
|
||||
As we have to iterate over the vertex buffer to copy qreal to float
|
||||
anyway we reorder and drop the index buffer.
|
||||
|
||||
QTriangleSet:
|
||||
|
||||
vertices: (x[i[n]], y[i[n]]), (x[j[n]], y[j[n]]), (x[k[n]], y[k[n]]), n = 0, 1, ...
|
||||
QVector<qreal> vertices; // [x[0], y[0], x[1], y[1], x[2], ...]
|
||||
QVector<quint16> indices; // [i[0], j[0], k[0], i[1], j[1], k[1], i[2], ...]
|
||||
*/
|
||||
const auto points = ts.vertices.constData();
|
||||
const auto indices = reinterpret_cast< const quint16* >( ts.indices.data() );
|
||||
|
||||
geometry.allocate( ts.indices.size() );
|
||||
|
||||
auto vertexData = geometry.vertexDataAsPoint2D();
|
||||
for ( int i = 0; i < ts.indices.size(); i++ )
|
||||
{
|
||||
const int j = 2 * indices[i];
|
||||
vertexData[i].set( points[j], points[j + 1] );
|
||||
}
|
||||
}
|
||||
|
||||
static inline void qskResetGeometry( QskBoxFillNode* node )
|
||||
{
|
||||
auto g = node->geometry();
|
||||
if ( g->vertexCount() > 0 )
|
||||
{
|
||||
g->allocate( 0 );
|
||||
node->markDirty( QSGNode::DirtyGeometry );
|
||||
}
|
||||
}
|
||||
|
||||
class QskBoxFillNodePrivate final : public QSGGeometryNodePrivate
|
||||
{
|
||||
public:
|
||||
QskBoxFillNodePrivate()
|
||||
: geometry( QSGGeometry::defaultAttributes_Point2D(), 0 )
|
||||
{
|
||||
geometry.setDrawingMode( QSGGeometry::DrawTriangles );
|
||||
}
|
||||
|
||||
QskHashValue metricsHash = 0;
|
||||
QskHashValue gradientHash = 0;
|
||||
QRectF rect;
|
||||
|
||||
QSGGeometry geometry;
|
||||
int gradientType = -1;
|
||||
};
|
||||
|
||||
QskBoxFillNode::QskBoxFillNode()
|
||||
: QSGGeometryNode( *new QskBoxFillNodePrivate )
|
||||
{
|
||||
Q_D( QskBoxFillNode );
|
||||
|
||||
setGeometry( &d->geometry );
|
||||
setMaterial( new QSGFlatColorMaterial() );
|
||||
setFlag( QSGNode::OwnsMaterial, true );
|
||||
}
|
||||
|
||||
void QskBoxFillNode::updateNode(
|
||||
const QRectF& rect, const QskBoxShapeMetrics& shapeMetrics,
|
||||
const QskBoxBorderMetrics& borderMetrics, const QskGradient& gradient )
|
||||
{
|
||||
Q_D( QskBoxFillNode );
|
||||
|
||||
const auto effectiveGradient = qskEffectiveGradient( gradient );
|
||||
|
||||
const auto metricsHash = qskMetricsHash( shapeMetrics, borderMetrics );
|
||||
const auto gradientHash = effectiveGradient.hash( 17321 );
|
||||
|
||||
const bool dirtyGeometry = ( metricsHash != d->metricsHash ) || ( rect == d->rect );
|
||||
const bool dirtyMaterial = gradientHash != d->gradientHash;
|
||||
|
||||
if ( !( dirtyGeometry || dirtyMaterial ) )
|
||||
return;
|
||||
|
||||
d->metricsHash = metricsHash;
|
||||
d->gradientHash = gradientHash;
|
||||
d->rect = rect;
|
||||
|
||||
if ( rect.isEmpty() || !effectiveGradient.isVisible() )
|
||||
{
|
||||
qskResetGeometry( this );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( dirtyGeometry )
|
||||
{
|
||||
const auto path = QskBoxRenderer().fillPath(
|
||||
rect, shapeMetrics, borderMetrics );
|
||||
|
||||
if ( path.isEmpty() )
|
||||
{
|
||||
qskResetGeometry( this );
|
||||
return;
|
||||
}
|
||||
|
||||
qskUpdateGeometry( path, d->geometry );
|
||||
|
||||
markDirty( QSGNode::DirtyGeometry );
|
||||
}
|
||||
|
||||
if ( dirtyMaterial )
|
||||
{
|
||||
if ( effectiveGradient.isMonochrome() )
|
||||
{
|
||||
if ( material() == nullptr || d->gradientType >= 0 )
|
||||
{
|
||||
setMaterial( new QSGFlatColorMaterial() );
|
||||
d->gradientType = -1;
|
||||
}
|
||||
|
||||
auto colorMaterial = static_cast< QSGFlatColorMaterial* >( material() );
|
||||
colorMaterial->setColor( effectiveGradient.startColor().toRgb() );
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto gradientType = effectiveGradient.type();
|
||||
|
||||
if ( ( material() == nullptr ) || ( gradientType != d->gradientType ) )
|
||||
{
|
||||
setMaterial( QskGradientMaterial::createMaterial( gradientType ) );
|
||||
d->gradientType = gradientType;
|
||||
}
|
||||
|
||||
auto gradientMaterial = static_cast< QskGradientMaterial* >( material() );
|
||||
gradientMaterial->updateGradient( rect, effectiveGradient );
|
||||
}
|
||||
|
||||
markDirty( QSGNode::DirtyMaterial );
|
||||
}
|
||||
}
|
35
src/nodes/QskBoxFillNode.h
Normal file
35
src/nodes/QskBoxFillNode.h
Normal file
@ -0,0 +1,35 @@
|
||||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
||||
* This file may be used under the terms of the QSkinny License, Version 1.0
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef QSK_BOX_FILL_NODE_H
|
||||
#define QSK_BOX_FILL_NODE_H
|
||||
|
||||
#include "QskGlobal.h"
|
||||
#include <qsgnode.h>
|
||||
|
||||
class QskGradient;
|
||||
class QskBoxShapeMetrics;
|
||||
class QskBoxBorderMetrics;
|
||||
|
||||
class QskBoxFillNodePrivate;
|
||||
|
||||
class QSK_EXPORT QskBoxFillNode : public QSGGeometryNode
|
||||
{
|
||||
public:
|
||||
QskBoxFillNode();
|
||||
|
||||
void updateNode( const QRectF&,
|
||||
const QskBoxShapeMetrics&, const QskBoxBorderMetrics&,
|
||||
const QskGradient& );
|
||||
|
||||
void updateNode( const QRectF&,
|
||||
const QskBoxShapeMetrics&, const QskBoxBorderMetrics&,
|
||||
const QColor& );
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE( QskBoxFillNode )
|
||||
};
|
||||
|
||||
#endif
|
@ -4,14 +4,92 @@
|
||||
*****************************************************************************/
|
||||
|
||||
#include "QskShadedBoxNode.h"
|
||||
#include "QskBoxFillNode.h"
|
||||
#include "QskBoxShadowNode.h"
|
||||
#include "QskBoxNode.h"
|
||||
#include "QskSGNode.h"
|
||||
|
||||
#include "QskGradient.h"
|
||||
#include "QskGradientDirection.h"
|
||||
#include "QskShadowMetrics.h"
|
||||
#include <qcolor.h>
|
||||
#include "QskBoxBorderMetrics.h"
|
||||
#include "QskBoxBorderColors.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
enum Role
|
||||
{
|
||||
ShadowRole,
|
||||
BoxRole,
|
||||
FillRole
|
||||
};
|
||||
}
|
||||
|
||||
void qskUpdateChildren( QSGNode* parentNode, quint8 role, QSGNode* node )
|
||||
{
|
||||
static const QVector< quint8 > roles = { ShadowRole, BoxRole, FillRole };
|
||||
|
||||
auto oldNode = QskSGNode::findChildNode( parentNode, role );
|
||||
QskSGNode::replaceChildNode( roles, role, parentNode, oldNode, node );
|
||||
}
|
||||
|
||||
template< typename Node >
|
||||
inline Node* qskNode( QSGNode* parentNode, quint8 role )
|
||||
{
|
||||
using namespace QskSGNode;
|
||||
|
||||
auto node = static_cast< Node* > ( findChildNode( parentNode, ShadowRole ) );
|
||||
|
||||
if ( node == nullptr )
|
||||
{
|
||||
node = new Node();
|
||||
setNodeRole( node, role );
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static inline void qskInsertNode( QSGNode* parentNode, QSGNode* node )
|
||||
{
|
||||
static const QVector< quint8 > roles = { ShadowRole, BoxRole, FillRole };
|
||||
|
||||
QskSGNode::replaceChildNode( roles, QskSGNode::nodeRole( node ),
|
||||
parentNode, nullptr, node );
|
||||
}
|
||||
|
||||
static inline bool qskIsBoxGradient( const QskGradient& gradient )
|
||||
{
|
||||
if ( !gradient.isVisible() || gradient.isMonochrome() )
|
||||
return true;
|
||||
|
||||
switch( gradient.type() )
|
||||
{
|
||||
case QskGradient::Linear:
|
||||
{
|
||||
auto dir = gradient.linearDirection();
|
||||
|
||||
if ( dir.isTilted() )
|
||||
{
|
||||
// only diagonal from topLeft to bottomRight
|
||||
return ( dir.x1() == dir.x2() ) && ( dir.y1() == dir.y2() );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
case QskGradient::Radial:
|
||||
case QskGradient::Conic:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QskShadedBoxNode::QskShadedBoxNode()
|
||||
{
|
||||
m_boxNode.setFlag( QSGNode::OwnedByParent, false );
|
||||
appendChildNode( &m_boxNode );
|
||||
}
|
||||
|
||||
QskShadedBoxNode::~QskShadedBoxNode()
|
||||
@ -23,27 +101,47 @@ void QskShadedBoxNode::setBoxData( const QRectF& rect,
|
||||
const QskBoxBorderColors& borderColors, const QskGradient& gradient,
|
||||
const QskShadowMetrics& shadowMetrics, const QColor& shadowColor )
|
||||
{
|
||||
m_boxNode.setBoxData( rect, shape, borderMetrics, borderColors, gradient );
|
||||
using namespace QskSGNode;
|
||||
|
||||
if ( shadowMetrics.isNull()
|
||||
|| !shadowColor.isValid() || shadowColor.alpha() == 0 )
|
||||
QskBoxShadowNode* shadowNode = nullptr;
|
||||
QskBoxNode* boxNode = nullptr;
|
||||
QskBoxFillNode* fillNode = nullptr;
|
||||
|
||||
if ( !shadowMetrics.isNull()
|
||||
&& shadowColor.isValid() && shadowColor.alpha() != 0 )
|
||||
{
|
||||
if ( m_shadowNode )
|
||||
{
|
||||
removeChildNode( m_shadowNode );
|
||||
delete m_shadowNode;
|
||||
m_shadowNode = nullptr;
|
||||
}
|
||||
shadowNode = qskNode< QskBoxShadowNode >( this, ShadowRole );
|
||||
shadowNode->setShadowData( shadowMetrics.shadowRect( rect ),
|
||||
shape, shadowMetrics.blurRadius(), shadowColor );
|
||||
}
|
||||
|
||||
/*
|
||||
QskBoxNode supports vertical/horizontal/diagonal gradients only.
|
||||
If our gradient doesn't fall into this category we use a QskBoxFillNode.
|
||||
However the border is always done with a QskBoxNode
|
||||
*/
|
||||
|
||||
if ( qskIsBoxGradient( gradient ) )
|
||||
{
|
||||
boxNode = qskNode< QskBoxNode >( this, BoxRole );
|
||||
boxNode->setBoxData( rect, shape, borderMetrics, borderColors, gradient );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( m_shadowNode == nullptr )
|
||||
if ( !borderMetrics.isNull() && borderColors.isVisible() )
|
||||
{
|
||||
m_shadowNode = new QskBoxShadowNode();
|
||||
insertChildNodeBefore( m_shadowNode, &m_boxNode );
|
||||
boxNode = qskNode< QskBoxNode >( this, BoxRole );
|
||||
boxNode->setBoxData( rect, shape, borderMetrics, borderColors, QskGradient() );
|
||||
}
|
||||
|
||||
m_shadowNode->setShadowData( shadowMetrics.shadowRect( rect ),
|
||||
shape, shadowMetrics.blurRadius(), shadowColor );
|
||||
if ( gradient.isVisible() )
|
||||
{
|
||||
fillNode = qskNode< QskBoxFillNode >( this, FillRole );
|
||||
fillNode->updateNode( rect, shape, borderMetrics, gradient );
|
||||
}
|
||||
}
|
||||
|
||||
qskUpdateChildren( this, ShadowRole, shadowNode );
|
||||
qskUpdateChildren( this, BoxRole, boxNode );
|
||||
qskUpdateChildren( this, FillRole, fillNode );
|
||||
}
|
||||
|
@ -7,10 +7,15 @@
|
||||
#define QSK_SHADED_BOX_NODE_H
|
||||
|
||||
#include "QskGlobal.h"
|
||||
#include "QskBoxNode.h"
|
||||
#include <qsgnode.h>
|
||||
|
||||
class QskBoxShadowNode;
|
||||
class QskShadowMetrics;
|
||||
class QskBoxShapeMetrics;
|
||||
class QskBoxBorderMetrics;
|
||||
class QskBoxBorderColors;
|
||||
class QskGradient;
|
||||
class QskShadowMetrics;
|
||||
class QColor;
|
||||
|
||||
class QSK_EXPORT QskShadedBoxNode : public QSGNode
|
||||
{
|
||||
@ -22,10 +27,6 @@ class QSK_EXPORT QskShadedBoxNode : public QSGNode
|
||||
const QskBoxShapeMetrics&, const QskBoxBorderMetrics&,
|
||||
const QskBoxBorderColors&, const QskGradient&,
|
||||
const QskShadowMetrics&, const QColor& shadowColor );
|
||||
|
||||
private:
|
||||
QskBoxNode m_boxNode;
|
||||
QskBoxShadowNode* m_shadowNode = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -103,6 +103,7 @@ HEADERS += \
|
||||
nodes/QskArcRenderer.h \
|
||||
nodes/QskBoxNode.h \
|
||||
nodes/QskBoxClipNode.h \
|
||||
nodes/QskBoxFillNode.h \
|
||||
nodes/QskBoxRenderer.h \
|
||||
nodes/QskBoxRendererColorMap.h \
|
||||
nodes/QskBoxShadowNode.h \
|
||||
@ -128,6 +129,7 @@ SOURCES += \
|
||||
nodes/QskArcRenderer.cpp \
|
||||
nodes/QskBoxNode.cpp \
|
||||
nodes/QskBoxClipNode.cpp \
|
||||
nodes/QskBoxFillNode.cpp \
|
||||
nodes/QskBoxRendererRect.cpp \
|
||||
nodes/QskBoxRendererEllipse.cpp \
|
||||
nodes/QskBoxRendererDEllipse.cpp \
|
||||
|
Loading…
x
Reference in New Issue
Block a user