shaders for gradients ( QGradient variations ) added
This commit is contained in:
parent
f3acb55170
commit
b138d04053
@ -33,20 +33,17 @@ QPen ShapeItem::pen() const
|
||||
return m_pen;
|
||||
}
|
||||
|
||||
void ShapeItem::setFillColor( const QColor& color )
|
||||
void ShapeItem::setGradient( const QColor& c1, const QColor& c2 )
|
||||
{
|
||||
if ( color != m_fillColor )
|
||||
if ( c1 != m_fillColor[0] || c2 != m_fillColor[1] )
|
||||
{
|
||||
m_fillColor = color;
|
||||
m_fillColor[0] = c1;
|
||||
m_fillColor[1] = c2;
|
||||
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
QColor ShapeItem::fillColor() const
|
||||
{
|
||||
return m_fillColor;
|
||||
}
|
||||
|
||||
void ShapeItem::setPath( const QPainterPath& path )
|
||||
{
|
||||
if ( path != m_path )
|
||||
@ -92,7 +89,20 @@ void ShapeItem::updateNode( QSGNode* parentNode )
|
||||
QskSGNode::setNodeRole( fillNode, FillRole );
|
||||
}
|
||||
|
||||
fillNode->updateNode( path, m_fillColor );
|
||||
if ( m_fillColor[0] != m_fillColor[1] )
|
||||
{
|
||||
QLinearGradient gradient;
|
||||
gradient.setStart( rect.topLeft() );
|
||||
gradient.setFinalStop( rect.bottomRight() );
|
||||
gradient.setColorAt( 0.0, m_fillColor[0] );
|
||||
gradient.setColorAt( 1.0, m_fillColor[1] );
|
||||
|
||||
fillNode->updateNode( path, &gradient );
|
||||
}
|
||||
else
|
||||
{
|
||||
fillNode->updateNode( path, m_fillColor[0] );
|
||||
}
|
||||
|
||||
if ( fillNode->parent() != parentNode )
|
||||
parentNode->prependChildNode( fillNode );
|
||||
|
@ -20,8 +20,7 @@ class ShapeItem : public QskControl
|
||||
void setPen( const QPen& );
|
||||
QPen pen() const;
|
||||
|
||||
void setFillColor( const QColor& );
|
||||
QColor fillColor() const;
|
||||
void setGradient( const QColor&, const QColor& );
|
||||
|
||||
void setPath( const QPainterPath& );
|
||||
QPainterPath path() const;
|
||||
@ -33,6 +32,6 @@ class ShapeItem : public QskControl
|
||||
QPainterPath scaledPath( const QRectF& ) const;
|
||||
|
||||
QPen m_pen;
|
||||
QColor m_fillColor;
|
||||
QColor m_fillColor[2];
|
||||
QPainterPath m_path;
|
||||
};
|
||||
|
@ -36,8 +36,8 @@ int main( int argc, char* argv[] )
|
||||
auto shapeItem = new ShapeItem();
|
||||
|
||||
shapeItem->setPath( path( SkinnyShapeFactory::Hexagon ) );
|
||||
shapeItem->setPen( QPen( Qt::darkBlue, 20 ) );
|
||||
shapeItem->setFillColor( Qt::darkYellow );
|
||||
shapeItem->setPen( QPen( Qt::darkCyan, 20 ) );
|
||||
shapeItem->setGradient( Qt::red, Qt::blue );
|
||||
|
||||
window.addItem( shapeItem );
|
||||
window.resize( 600, 600 );
|
||||
|
812
src/nodes/QskGradientMaterial.cpp
Normal file
812
src/nodes/QskGradientMaterial.cpp
Normal file
@ -0,0 +1,812 @@
|
||||
#include "QskGradientMaterial.h"
|
||||
#include "QskFunctions.h"
|
||||
#include "QskRgbValue.h"
|
||||
|
||||
QSK_QT_PRIVATE_BEGIN
|
||||
#include <private/qrhi_p.h>
|
||||
#include <private/qdrawhelper_p.h>
|
||||
#include <private/qsgplaintexture_p.h>
|
||||
QSK_QT_PRIVATE_END
|
||||
|
||||
// RHI shaders are supported by Qt 5.15 and Qt 6.x
|
||||
#define SHADER_RHI
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
|
||||
// Old type of shaders only with Qt 5.x
|
||||
#define SHADER_GL
|
||||
#endif
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
|
||||
#include <QSGMaterialRhiShader>
|
||||
using RhiShader = QSGMaterialRhiShader;
|
||||
#else
|
||||
using RhiShader = QSGMaterialShader;
|
||||
#endif
|
||||
|
||||
/*
|
||||
The shaders for the gradient are implemented in the quickshapes module
|
||||
Beside them we do not need this module.
|
||||
*/
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
|
||||
|
||||
QSK_QT_PRIVATE_BEGIN
|
||||
#ifndef signals
|
||||
#define signals Q_SIGNALS
|
||||
#endif
|
||||
#include <private/qquickshape_p.h>
|
||||
QSK_QT_PRIVATE_END
|
||||
|
||||
static void QQuickShapes_initializeModule()
|
||||
{
|
||||
QQuickShapesModule::defineModule();
|
||||
}
|
||||
|
||||
#else
|
||||
extern void QQuickShapes_initializeModule();
|
||||
#endif
|
||||
|
||||
Q_CONSTRUCTOR_FUNCTION( QQuickShapes_initializeModule )
|
||||
|
||||
namespace
|
||||
{
|
||||
/*
|
||||
Qt offers QGradient compatible shaders in the quickshapes module.
|
||||
So why reimplementing the wheel ...
|
||||
|
||||
Those shaders ( f.e lineargradient.frag ) want to have a lookup table
|
||||
with the interpolated colors, being uploaded as a texture.
|
||||
A 1 dimensional 256x1 texture is how Chrome, Firefox, and Android render
|
||||
gradients - so let's do the same.
|
||||
*/
|
||||
class GradientImage : public QImage
|
||||
{
|
||||
public:
|
||||
GradientImage( const QGradientStops& stops )
|
||||
: QImage( 256, 1, QImage::Format_RGBA8888_Premultiplied )
|
||||
{
|
||||
constexpr int numColors = 256;
|
||||
auto colorTable = reinterpret_cast< uint* >( bits() );
|
||||
|
||||
int index1, index2;
|
||||
QRgb rgb1, rgb2;
|
||||
|
||||
index1 = index2 = qRound( stops[0].first * numColors );
|
||||
rgb1 = rgb2 = stops[0].second.rgba();
|
||||
|
||||
if ( index1 > 0 )
|
||||
{
|
||||
const auto v = value( rgb1 );
|
||||
|
||||
for ( int i = 0; i < index1; i++ )
|
||||
colorTable[i] = v;
|
||||
}
|
||||
|
||||
for ( int i = 1; i < stops.count(); i++ )
|
||||
{
|
||||
const auto& stop = stops[i];
|
||||
|
||||
index2 = qRound( stop.first * numColors );
|
||||
rgb2 = stop.second.rgba();
|
||||
|
||||
const auto n = index2 - index1;
|
||||
|
||||
for ( int j = 0; j < n; j++ )
|
||||
{
|
||||
const auto rgb = QskRgb::interpolated( rgb1, rgb2, qreal( j ) / n );
|
||||
colorTable[ index1 + j] = value( rgb );
|
||||
}
|
||||
|
||||
index1 = index2;
|
||||
rgb1 = rgb2;
|
||||
}
|
||||
|
||||
if ( index1 < numColors - 1 )
|
||||
{
|
||||
const auto v = value( rgb1 );
|
||||
|
||||
for ( int i = index1; i < numColors ; i++ )
|
||||
colorTable[i] = v;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
inline uint value( const QRgb rgb ) const
|
||||
{
|
||||
return ARGB2RGBA( qPremultiply( rgb ) );
|
||||
}
|
||||
};
|
||||
|
||||
class GradientTexture : public QSGPlainTexture
|
||||
{
|
||||
public:
|
||||
GradientTexture( const QGradientStops& stops, QGradient::Spread spread )
|
||||
{
|
||||
setImage( GradientImage( stops ) );
|
||||
|
||||
const auto wrapMode = this->wrapMode( spread );
|
||||
|
||||
setHorizontalWrapMode( wrapMode );
|
||||
setVerticalWrapMode( wrapMode );
|
||||
|
||||
setFiltering( QSGTexture::Linear );
|
||||
};
|
||||
|
||||
private:
|
||||
static inline QSGTexture::WrapMode wrapMode( QGradient::Spread spread )
|
||||
{
|
||||
switch ( spread )
|
||||
{
|
||||
case QGradient::RepeatSpread:
|
||||
return QSGTexture::Repeat;
|
||||
|
||||
case QGradient::ReflectSpread:
|
||||
return QSGTexture::MirroredRepeat;
|
||||
|
||||
default:
|
||||
return QSGTexture::ClampToEdge;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class TextureHashKey
|
||||
{
|
||||
public:
|
||||
inline bool operator==( const TextureHashKey& other ) const
|
||||
{
|
||||
return rhi == other.rhi && spread == other.spread && stops == other.stops;
|
||||
}
|
||||
|
||||
const void* rhi;
|
||||
const QGradientStops stops;
|
||||
const QGradient::Spread spread;
|
||||
};
|
||||
|
||||
inline size_t qHash( const TextureHashKey& key, size_t seed = 0 )
|
||||
{
|
||||
size_t valus = seed + key.spread;
|
||||
|
||||
for ( const auto& stop : key.stops )
|
||||
valus += stop.second.rgba();
|
||||
|
||||
return valus;
|
||||
}
|
||||
|
||||
class TextureCache
|
||||
{
|
||||
public:
|
||||
static TextureCache* instance()
|
||||
{
|
||||
static TextureCache* s_instance = nullptr;
|
||||
if ( s_instance == nullptr )
|
||||
{
|
||||
s_instance = new TextureCache();
|
||||
|
||||
/*
|
||||
For OpenGL we coud fiddle around with QOpenGLSharedResource
|
||||
while with RHI we would have QRhi::addCleanupCallback
|
||||
|
||||
But let's keep things simple for the moment. TODO ...
|
||||
*/
|
||||
qAddPostRoutine( cleanup );
|
||||
}
|
||||
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
~TextureCache()
|
||||
{
|
||||
qDeleteAll( m_hashTable );
|
||||
}
|
||||
|
||||
GradientTexture* texture( const void* rhi,
|
||||
const QGradientStops& stops, QGradient::Spread spread )
|
||||
{
|
||||
const TextureHashKey key { rhi, stops, spread };
|
||||
|
||||
auto texture = m_hashTable[key];
|
||||
if ( texture == nullptr )
|
||||
{
|
||||
texture = new GradientTexture( stops, spread );
|
||||
m_hashTable[ key ] = texture;
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
private:
|
||||
static void cleanup()
|
||||
{
|
||||
delete instance();
|
||||
}
|
||||
|
||||
QHash< TextureHashKey, GradientTexture* > m_hashTable;
|
||||
};
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class GradientMaterial : public QskGradientMaterial
|
||||
{
|
||||
public:
|
||||
GradientMaterial( QGradient::Type type,
|
||||
const QGradientStops& stops, QGradient::Spread spread )
|
||||
: QskGradientMaterial( type, stops, spread )
|
||||
{
|
||||
setFlag( Blending | RequiresFullMatrix );
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
|
||||
setFlag( QSGMaterial::SupportsRhiShader, true );
|
||||
#endif
|
||||
}
|
||||
|
||||
int compare( const QSGMaterial* other ) const override
|
||||
{
|
||||
const auto mat = static_cast< const GradientMaterial* >( other );
|
||||
|
||||
if ( ( spread() == mat->spread() ) && ( stops() == mat->stops() ) )
|
||||
return 0;
|
||||
|
||||
return QSGMaterial::compare( other );
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
|
||||
// make Qt 5/6 APIs matchaing
|
||||
|
||||
QSGMaterialShader* createShader(
|
||||
QSGRendererInterface::RenderMode ) const override final
|
||||
{
|
||||
return createShader();
|
||||
}
|
||||
|
||||
virtual QSGMaterialShader* createShader() const = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef SHADER_GL
|
||||
|
||||
class GradientShaderGL : public QSGMaterialShader
|
||||
{
|
||||
public:
|
||||
void setShaderFiles( const char* name )
|
||||
{
|
||||
static const QString root( ":/qt-project.org/shapes/shaders/" );
|
||||
|
||||
setShaderSourceFile( QOpenGLShader::Vertex, root + name + ".vert" );
|
||||
setShaderSourceFile( QOpenGLShader::Fragment, root + name + ".frag" );
|
||||
}
|
||||
|
||||
void initialize() override
|
||||
{
|
||||
m_opacityId = program()->uniformLocation( "opacity" );
|
||||
m_matrixId = program()->uniformLocation( "matrix" );
|
||||
}
|
||||
|
||||
void updateState( const RenderState& state,
|
||||
QSGMaterial* newMaterial, QSGMaterial* ) override final
|
||||
{
|
||||
auto p = program();
|
||||
auto material = static_cast< GradientMaterial* >( newMaterial );
|
||||
|
||||
if ( state.isOpacityDirty() )
|
||||
p->setUniformValue( m_opacityId, state.opacity() );
|
||||
|
||||
if ( state.isMatrixDirty() )
|
||||
p->setUniformValue(m_matrixId, state.combinedMatrix() );
|
||||
|
||||
updateUniformValues( material );
|
||||
|
||||
auto texture = TextureCache::instance()->texture(
|
||||
nullptr, material->stops(), material->spread() );
|
||||
texture->bind();
|
||||
}
|
||||
|
||||
char const* const* attributeNames() const override final
|
||||
{
|
||||
static const char* const attr[] = { "vertexCoord", nullptr };
|
||||
return attr;
|
||||
}
|
||||
|
||||
virtual void updateUniformValues( const GradientMaterial* ) = 0;
|
||||
|
||||
protected:
|
||||
int m_opacityId = -1;
|
||||
int m_matrixId = -1;
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef SHADER_RHI
|
||||
class GradientShaderRhi : public RhiShader
|
||||
{
|
||||
public:
|
||||
void setShaderFiles( const char* name )
|
||||
{
|
||||
static const QString root( ":/qt-project.org/shapes/shaders_ng/" );
|
||||
|
||||
setShaderFileName( VertexStage, root + name + ".vert.qsb" );
|
||||
setShaderFileName( FragmentStage, root + name + ".frag.qsb" );
|
||||
}
|
||||
|
||||
void updateSampledImage( RenderState& state, int binding,
|
||||
QSGTexture** texture, QSGMaterial* newMaterial, QSGMaterial*) override final
|
||||
{
|
||||
if ( binding != 1 )
|
||||
return;
|
||||
|
||||
auto material = static_cast< const GradientMaterial* >( newMaterial );
|
||||
|
||||
auto txt = TextureCache::instance()->texture(
|
||||
state.rhi(), material->stops(), material->spread() );
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
|
||||
txt->updateRhiTexture( state.rhi(), state.resourceUpdateBatch() );
|
||||
#else
|
||||
txt->commitTextureOperations( state.rhi(), state.resourceUpdateBatch() );
|
||||
#endif
|
||||
|
||||
*texture = txt;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class LinearMaterial final : public GradientMaterial
|
||||
{
|
||||
public:
|
||||
LinearMaterial(
|
||||
const QGradientStops& stops, QGradient::Spread spread,
|
||||
const QPointF& start, const QPointF& stop )
|
||||
: GradientMaterial( QGradient::LinearGradient, stops, spread )
|
||||
, m_start( start )
|
||||
, m_stop( stop )
|
||||
{
|
||||
}
|
||||
|
||||
QSGMaterialShader* createShader() const override;
|
||||
|
||||
QSGMaterialType* type() const override
|
||||
{
|
||||
static QSGMaterialType type;
|
||||
return &type;
|
||||
}
|
||||
|
||||
int compare( const QSGMaterial* other ) const override
|
||||
{
|
||||
const auto mat = static_cast< const LinearMaterial* >( other );
|
||||
|
||||
if ( ( m_start != mat->m_start ) || ( m_stop != mat->m_stop ) )
|
||||
return QSGMaterial::compare( other );
|
||||
else
|
||||
return GradientMaterial::compare( other );
|
||||
}
|
||||
|
||||
const QVector2D m_start;
|
||||
const QVector2D m_stop;
|
||||
};
|
||||
|
||||
#ifdef SHADER_GL
|
||||
class LinearShaderGL final : public GradientShaderGL
|
||||
{
|
||||
public:
|
||||
LinearShaderGL()
|
||||
{
|
||||
setShaderFiles( "lineargradient" );
|
||||
}
|
||||
|
||||
void initialize() override
|
||||
{
|
||||
GradientShaderGL::initialize();
|
||||
|
||||
m_startId = program()->uniformLocation( "gradStart" );
|
||||
m_stopId = program()->uniformLocation( "gradEnd" );
|
||||
}
|
||||
|
||||
void updateUniformValues( const GradientMaterial* newMaterial ) override
|
||||
{
|
||||
auto material = static_cast< const LinearMaterial* >( newMaterial );
|
||||
|
||||
program()->setUniformValue( m_startId, material->m_start );
|
||||
program()->setUniformValue( m_stopId, material->m_stop );
|
||||
}
|
||||
|
||||
private:
|
||||
int m_startId = -1;
|
||||
int m_stopId = -1;
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef SHADER_RHI
|
||||
class LinearShaderRhi final : public GradientShaderRhi
|
||||
{
|
||||
public:
|
||||
LinearShaderRhi()
|
||||
{
|
||||
setShaderFiles( "lineargradient" );
|
||||
}
|
||||
|
||||
bool updateUniformData( RenderState& state,
|
||||
QSGMaterial* newMaterial, QSGMaterial* oldMaterial ) override
|
||||
{
|
||||
auto matNew = static_cast< LinearMaterial* >( newMaterial );
|
||||
auto matOld = static_cast< LinearMaterial* >( oldMaterial );
|
||||
|
||||
Q_ASSERT( state.uniformData()->size() >= 84 );
|
||||
|
||||
auto data = state.uniformData()->data();
|
||||
bool changed = false;
|
||||
|
||||
if ( state.isMatrixDirty() )
|
||||
{
|
||||
const auto matrix = state.combinedMatrix();
|
||||
memcpy( data + 0, matrix.constData(), 64 );
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if ( matOld == nullptr || matNew->m_start != matOld->m_start )
|
||||
{
|
||||
memcpy( data + 64, &matNew->m_start, 8 );
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if ( matOld == nullptr || matNew->m_stop != matOld->m_stop )
|
||||
{
|
||||
memcpy( data + 72, &matNew->m_stop, 8 );
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if ( state.isOpacityDirty() )
|
||||
{
|
||||
const float opacity = state.opacity();
|
||||
memcpy( data + 80, &opacity, 4 );
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
QSGMaterialShader* LinearMaterial::createShader() const
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
|
||||
if ( !( flags() & QSGMaterial::RhiShaderWanted ) )
|
||||
return new LinearShaderGL;
|
||||
#endif
|
||||
return new LinearShaderRhi;
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class RadialMaterial final : public GradientMaterial
|
||||
{
|
||||
public:
|
||||
RadialMaterial(
|
||||
const QGradientStops& stops, QGradient::Spread spread,
|
||||
const QPointF& center, qreal centerRadius,
|
||||
const QPointF& focalPoint, qreal focalRadius )
|
||||
: GradientMaterial( QGradient::RadialGradient, stops, spread )
|
||||
, m_center( center )
|
||||
, m_focalPoint( focalPoint )
|
||||
, m_centerRadius( centerRadius )
|
||||
, m_focalRadius( focalRadius )
|
||||
{
|
||||
}
|
||||
|
||||
QSGMaterialShader* createShader() const override;
|
||||
|
||||
QSGMaterialType* type() const override
|
||||
{
|
||||
static QSGMaterialType type;
|
||||
return &type;
|
||||
}
|
||||
|
||||
int compare( const QSGMaterial* other ) const override
|
||||
{
|
||||
const auto mat = static_cast< const RadialMaterial* >( other );
|
||||
|
||||
if ( ( m_center != mat->m_center )
|
||||
|| ( m_focalPoint != mat->m_focalPoint )
|
||||
|| qskFuzzyCompare( m_centerRadius, mat->m_centerRadius )
|
||||
|| qskFuzzyCompare( m_focalRadius, mat->m_focalRadius ) )
|
||||
{
|
||||
return QSGMaterial::compare( other );
|
||||
}
|
||||
else
|
||||
{
|
||||
return GradientMaterial::compare( other );
|
||||
}
|
||||
}
|
||||
|
||||
const QVector2D m_center;
|
||||
const QVector2D m_focalPoint;
|
||||
const float m_centerRadius;
|
||||
const float m_focalRadius;
|
||||
};
|
||||
|
||||
#ifdef SHADER_GL
|
||||
class RadialShaderGL final : public GradientShaderGL
|
||||
{
|
||||
public:
|
||||
RadialShaderGL()
|
||||
{
|
||||
setShaderFiles( "radialgradient" );
|
||||
}
|
||||
|
||||
void initialize() override
|
||||
{
|
||||
GradientShaderGL::initialize();
|
||||
|
||||
auto p = program();
|
||||
|
||||
m_centerPointId = p->uniformLocation( "translationPoint" );
|
||||
m_focalPointId = p->uniformLocation( "focalToCenter" );
|
||||
m_centerRadiusId = p->uniformLocation( "centerRadius" );
|
||||
m_focalRadiusId = p->uniformLocation( "focalRadius" );
|
||||
}
|
||||
|
||||
void updateUniformValues( const GradientMaterial* newMaterial ) override
|
||||
{
|
||||
auto material = static_cast< const RadialMaterial* >( newMaterial );
|
||||
|
||||
auto p = program();
|
||||
|
||||
p->setUniformValue( m_centerPointId, material->m_center );
|
||||
p->setUniformValue( m_centerRadiusId, material->m_centerRadius);
|
||||
p->setUniformValue( m_focalRadiusId, material->m_focalRadius);
|
||||
p->setUniformValue( m_focalPointId, material->m_focalPoint);
|
||||
}
|
||||
|
||||
private:
|
||||
int m_centerPointId = -1;
|
||||
int m_focalPointId = -1;
|
||||
int m_centerRadiusId = -1;
|
||||
int m_focalRadiusId = -1;
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef SHADER_RHI
|
||||
class RadialShaderRhi final : public GradientShaderRhi
|
||||
{
|
||||
public:
|
||||
RadialShaderRhi()
|
||||
{
|
||||
setShaderFiles( "radialgradient" );
|
||||
}
|
||||
|
||||
bool updateUniformData( RenderState& state,
|
||||
QSGMaterial* newMaterial, QSGMaterial* oldMaterial) override
|
||||
{
|
||||
auto matNew = static_cast< RadialMaterial* >( newMaterial );
|
||||
auto matOld = static_cast< RadialMaterial* >( oldMaterial );
|
||||
|
||||
Q_ASSERT( state.uniformData()->size() >= 92 );
|
||||
|
||||
auto data = state.uniformData()->data();
|
||||
bool changed = false;
|
||||
|
||||
if ( state.isMatrixDirty() )
|
||||
{
|
||||
const auto matrix = state.combinedMatrix();
|
||||
memcpy( data + 0, matrix.constData(), 64 );
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if ( matOld == nullptr || matNew->m_focalPoint != matOld->m_focalPoint )
|
||||
{
|
||||
memcpy( data + 64, &matNew->m_focalPoint, 8 );
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if ( matOld == nullptr || matNew->m_focalPoint != matOld->m_focalPoint )
|
||||
{
|
||||
memcpy( data + 72, &matNew->m_focalPoint, 8 );
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if ( matOld == nullptr || matNew->m_centerRadius != matOld->m_centerRadius )
|
||||
{
|
||||
memcpy( data + 80, &matNew->m_centerRadius, 4);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if ( matOld == nullptr || matNew->m_focalRadius != matOld->m_focalRadius )
|
||||
{
|
||||
memcpy( data + 84, &matOld->m_focalRadius, 4 );
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if ( state.isOpacityDirty() )
|
||||
{
|
||||
const float opacity = state.opacity();
|
||||
memcpy( data + 88, &opacity, 4 );
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
QSGMaterialShader* RadialMaterial::createShader() const
|
||||
{
|
||||
#ifdef SHADER_GL
|
||||
if ( !( flags() & QSGMaterial::RhiShaderWanted ) )
|
||||
return new RadialShaderGL;
|
||||
#endif
|
||||
|
||||
return new RadialShaderRhi;
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class ConicalMaterial final : public GradientMaterial
|
||||
{
|
||||
public:
|
||||
ConicalMaterial( const QGradientStops& stops,
|
||||
const QPointF& center, qreal angle )
|
||||
: GradientMaterial( QGradient::ConicalGradient, stops, QGradient::PadSpread )
|
||||
, m_center( center )
|
||||
, m_radians( -qDegreesToRadians( angle ) )
|
||||
{
|
||||
}
|
||||
|
||||
QSGMaterialShader* createShader() const override;
|
||||
|
||||
QSGMaterialType* type() const override
|
||||
{
|
||||
static QSGMaterialType type;
|
||||
return &type;
|
||||
}
|
||||
|
||||
int compare( const QSGMaterial* other ) const override
|
||||
{
|
||||
const auto mat = static_cast< const ConicalMaterial* >( other );
|
||||
|
||||
if ( ( m_center != mat->m_center ) || qskFuzzyCompare( m_radians, mat->m_radians ) )
|
||||
return QSGMaterial::compare( other );
|
||||
else
|
||||
return GradientMaterial::compare( other );
|
||||
}
|
||||
|
||||
const QVector2D m_center;
|
||||
const float m_radians;
|
||||
};
|
||||
|
||||
#ifdef SHADER_GL
|
||||
class ConicalShaderGL final : public GradientShaderGL
|
||||
{
|
||||
public:
|
||||
ConicalShaderGL()
|
||||
{
|
||||
setShaderFiles( "conicalgradient" );
|
||||
}
|
||||
|
||||
void initialize() override
|
||||
{
|
||||
GradientShaderGL::initialize();
|
||||
|
||||
m_radiansId = program()->uniformLocation( "angle" );
|
||||
m_centerPointId = program()->uniformLocation( "translationPoint" );
|
||||
}
|
||||
|
||||
void updateUniformValues( const GradientMaterial* newMaterial ) override
|
||||
{
|
||||
auto material = static_cast< const ConicalMaterial* >( newMaterial );
|
||||
|
||||
program()->setUniformValue( m_radiansId, material->m_radians );
|
||||
program()->setUniformValue( m_centerPointId, material->m_center );
|
||||
}
|
||||
|
||||
private:
|
||||
int m_radiansId = -1;
|
||||
int m_centerPointId = -1;
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef SHADER_RHI
|
||||
class ConicalShaderRhi final : public GradientShaderRhi
|
||||
{
|
||||
public:
|
||||
ConicalShaderRhi()
|
||||
{
|
||||
setShaderFiles( "conicalgradient" );
|
||||
}
|
||||
|
||||
bool updateUniformData( RenderState& state,
|
||||
QSGMaterial* newMaterial, QSGMaterial* oldMaterial ) override
|
||||
{
|
||||
auto matNew = static_cast< ConicalMaterial* >( newMaterial );
|
||||
auto matOld = static_cast< ConicalMaterial* >( oldMaterial );
|
||||
|
||||
Q_ASSERT( state.uniformData()->size() >= 80 );
|
||||
|
||||
auto data = state.uniformData()->data();
|
||||
bool changed = false;
|
||||
|
||||
if ( state.isMatrixDirty() )
|
||||
{
|
||||
const auto matrix = state.combinedMatrix();
|
||||
memcpy( data + 0, matrix.constData(), 64 );
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if ( matOld == nullptr || matNew->m_center != matOld->m_center )
|
||||
{
|
||||
memcpy( data + 64, &matNew->m_center, 8 );
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if ( matOld == nullptr || matNew->m_radians != matOld->m_radians )
|
||||
{
|
||||
memcpy( data + 72, &matNew->m_radians, 4 );
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if ( state.isOpacityDirty() )
|
||||
{
|
||||
const float opacity = state.opacity();
|
||||
memcpy( data + 76, &opacity, 4 );
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
QSGMaterialShader* ConicalMaterial::createShader() const
|
||||
{
|
||||
#ifdef SHADER_GL
|
||||
if ( !( flags() & QSGMaterial::RhiShaderWanted ) )
|
||||
return new ConicalShaderGL;
|
||||
#endif
|
||||
return new ConicalShaderRhi;
|
||||
}
|
||||
}
|
||||
|
||||
QskGradientMaterial* QskGradientMaterial::create( const QGradient* gradient )
|
||||
{
|
||||
switch ( static_cast< int >( gradient->type() ) )
|
||||
{
|
||||
case QGradient::LinearGradient:
|
||||
{
|
||||
auto linearGradient = static_cast< const QLinearGradient* >( gradient );
|
||||
|
||||
return new LinearMaterial(
|
||||
linearGradient->stops(), linearGradient->spread(),
|
||||
linearGradient->start(), linearGradient->finalStop() );
|
||||
}
|
||||
|
||||
case QGradient::RadialGradient:
|
||||
{
|
||||
auto radialGradient = static_cast< const QRadialGradient* >( gradient );
|
||||
|
||||
return new RadialMaterial(
|
||||
radialGradient->stops(), radialGradient->spread(),
|
||||
radialGradient->center(), radialGradient->centerRadius(),
|
||||
radialGradient->focalPoint(), radialGradient->focalRadius() );
|
||||
}
|
||||
|
||||
case QGradient::ConicalGradient:
|
||||
{
|
||||
auto conicalGradient = static_cast< const QConicalGradient* >( gradient );
|
||||
|
||||
return new ConicalMaterial( conicalGradient->stops(),
|
||||
conicalGradient->center(), conicalGradient->angle() );
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
56
src/nodes/QskGradientMaterial.h
Normal file
56
src/nodes/QskGradientMaterial.h
Normal file
@ -0,0 +1,56 @@
|
||||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
||||
* This file may be used under the terms of the QSkinny License, Version 1.0
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef QSK_GRADIENT_MATERIAL
|
||||
#define QSK_GRADIENT_MATERIAL
|
||||
|
||||
#include "QskGlobal.h"
|
||||
#include <qbrush.h>
|
||||
#include <qsgmaterial.h>
|
||||
|
||||
class QSK_EXPORT QskGradientMaterial : public QSGMaterial
|
||||
{
|
||||
public:
|
||||
static QskGradientMaterial* create( const QGradient* );
|
||||
|
||||
QGradient::Type gradientType() const;
|
||||
|
||||
const QGradientStops& stops() const;
|
||||
QGradient::Spread spread() const;
|
||||
|
||||
protected:
|
||||
QskGradientMaterial( QGradient::Type,
|
||||
const QGradientStops&, QGradient::Spread );
|
||||
|
||||
private:
|
||||
const QGradient::Type m_gradientType;
|
||||
const QGradientStops m_stops;
|
||||
const QGradient::Spread m_spread;
|
||||
};
|
||||
|
||||
inline QskGradientMaterial::QskGradientMaterial( QGradient::Type type,
|
||||
const QGradientStops& stops, QGradient::Spread spread )
|
||||
: m_gradientType( type )
|
||||
, m_stops( stops )
|
||||
, m_spread( spread )
|
||||
{
|
||||
}
|
||||
|
||||
inline QGradient::Type QskGradientMaterial::gradientType() const
|
||||
{
|
||||
return m_gradientType;
|
||||
}
|
||||
|
||||
inline const QGradientStops& QskGradientMaterial::stops() const
|
||||
{
|
||||
return m_stops;
|
||||
}
|
||||
|
||||
inline QGradient::Spread QskGradientMaterial::spread() const
|
||||
{
|
||||
return m_spread;
|
||||
}
|
||||
|
||||
#endif
|
@ -4,7 +4,9 @@
|
||||
*****************************************************************************/
|
||||
|
||||
#include "QskShapeNode.h"
|
||||
#include "QskGradientMaterial.h"
|
||||
|
||||
#include <qbrush.h>
|
||||
#include <qsgflatcolormaterial.h>
|
||||
|
||||
QSK_QT_PRIVATE_BEGIN
|
||||
@ -17,30 +19,41 @@ static void qskUpdateGeometry( const QPainterPath& path, QSGGeometry& geometry )
|
||||
{
|
||||
const auto ts = qTriangulate( path, QTransform(), 1, false );
|
||||
|
||||
#if 1
|
||||
geometry.allocate( ts.vertices.size(), ts.indices.size() );
|
||||
|
||||
auto vertexData = reinterpret_cast< float* >( geometry.vertexData() );
|
||||
const auto points = ts.vertices.constData();
|
||||
|
||||
for ( int i = 0; i < ts.vertices.count(); i++ )
|
||||
vertexData[i] = points[i];
|
||||
|
||||
memcpy( geometry.indexData(), ts.indices.data(),
|
||||
ts.indices.size() * sizeof( quint16 ) );
|
||||
#else
|
||||
/*
|
||||
As we have to iterate over the vertex buffer to copy qreal to float
|
||||
anyway we could reorder according to the index buffer and drop
|
||||
the index buffer then ???
|
||||
|
||||
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], ...]
|
||||
*/
|
||||
|
||||
geometry.allocate( ts.vertices.size(), ts.indices.size() );
|
||||
|
||||
auto vertexData = reinterpret_cast< float* >( geometry.vertexData() );
|
||||
const auto points = ts.vertices.constData();
|
||||
const auto indices = reinterpret_cast< const quint16* >( ts.indices.data() );
|
||||
|
||||
/*
|
||||
As we have to iterate over the vertex buffer to copy qreal to float
|
||||
anyway we could reorder according to the index buffer and drop
|
||||
the index buffer then ???
|
||||
*/
|
||||
geometry.allocate( ts.indices.size() );
|
||||
|
||||
for ( int i = 0; i < ts.vertices.count(); ++i )
|
||||
vertexData[i] = points[i];
|
||||
|
||||
memcpy( geometry.indexData(), ts.indices.data(),
|
||||
ts.indices.size() * sizeof( quint16 ) );
|
||||
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] );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
class QskShapeNodePrivate final : public QSGGeometryNodePrivate
|
||||
@ -82,3 +95,18 @@ void QskShapeNode::updateNode( const QPainterPath& path, const QColor& color )
|
||||
markDirty( QSGNode::DirtyMaterial );
|
||||
}
|
||||
}
|
||||
|
||||
void QskShapeNode::updateNode( const QPainterPath& path, const QGradient* gradient )
|
||||
{
|
||||
if ( true ) // For the moment we always update the geometry. TODO ...
|
||||
{
|
||||
qskUpdateGeometry( path, *geometry() );
|
||||
markDirty( QSGNode::DirtyGeometry );
|
||||
}
|
||||
|
||||
if ( true ) // For the moment we always update the material
|
||||
{
|
||||
setMaterial( QskGradientMaterial::create( gradient ) );
|
||||
markDirty( QSGNode::DirtyMaterial );
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "QskGlobal.h"
|
||||
#include <qsgnode.h>
|
||||
|
||||
class QGradient;
|
||||
class QColor;
|
||||
class QPainterPath;
|
||||
|
||||
@ -19,6 +20,7 @@ class QSK_EXPORT QskShapeNode : public QSGGeometryNode
|
||||
public:
|
||||
QskShapeNode();
|
||||
|
||||
void updateNode( const QPainterPath&, const QGradient* );
|
||||
void updateNode( const QPainterPath&, const QColor& );
|
||||
|
||||
private:
|
||||
|
12
src/src.pro
12
src/src.pro
@ -3,6 +3,12 @@ TARGET = $$qskLibraryTarget(qskinny)
|
||||
|
||||
QT += quick quick-private
|
||||
|
||||
# We need quickshapes for the shaders for the gradients only,
|
||||
# As those shaders are more or less trivial # it might be better
|
||||
# to implement our own shaders to get rid of this extra module dependency
|
||||
|
||||
QT += quickshapes-private
|
||||
|
||||
greaterThan( QT_MAJOR_VERSION, 5 ) {
|
||||
QT += opengl-private
|
||||
}
|
||||
@ -111,8 +117,9 @@ HEADERS += \
|
||||
nodes/QskScaleRenderer.h \
|
||||
nodes/QskSGNode.h \
|
||||
nodes/QskShadedBoxNode.h \
|
||||
nodes/QskShapeNode.h \
|
||||
nodes/QskStrokeNode.h \
|
||||
nodes/QskShapeNode.h \
|
||||
nodes/QskGradientMaterial.h \
|
||||
nodes/QskTextNode.h \
|
||||
nodes/QskTextRenderer.h \
|
||||
nodes/QskTextureRenderer.h \
|
||||
@ -135,8 +142,9 @@ SOURCES += \
|
||||
nodes/QskScaleRenderer.cpp \
|
||||
nodes/QskSGNode.cpp \
|
||||
nodes/QskShadedBoxNode.cpp \
|
||||
nodes/QskShapeNode.cpp \
|
||||
nodes/QskStrokeNode.cpp \
|
||||
nodes/QskShapeNode.cpp \
|
||||
nodes/QskGradientMaterial.cpp \
|
||||
nodes/QskTextNode.cpp \
|
||||
nodes/QskTextRenderer.cpp \
|
||||
nodes/QskTextureRenderer.cpp \
|
||||
|
Loading…
x
Reference in New Issue
Block a user