2022-09-28 20:14:10 +02:00
|
|
|
#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:
|
2022-09-29 12:40:22 +02:00
|
|
|
GradientMaterial( QGradient::Type type )
|
|
|
|
: QskGradientMaterial( type )
|
2022-09-28 20:14:10 +02:00
|
|
|
{
|
|
|
|
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,
|
2022-10-05 09:38:35 +02:00
|
|
|
QSGTexture* textures[], QSGMaterial* newMaterial, QSGMaterial*) override final
|
2022-09-28 20:14:10 +02:00
|
|
|
{
|
|
|
|
if ( binding != 1 )
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto material = static_cast< const GradientMaterial* >( newMaterial );
|
|
|
|
|
2022-10-05 09:38:35 +02:00
|
|
|
auto texture = TextureCache::instance()->texture(
|
2022-09-28 20:14:10 +02:00
|
|
|
state.rhi(), material->stops(), material->spread() );
|
|
|
|
|
|
|
|
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
|
2022-10-05 09:38:35 +02:00
|
|
|
texture->updateRhiTexture( state.rhi(), state.resourceUpdateBatch() );
|
2022-09-28 20:14:10 +02:00
|
|
|
#else
|
2022-10-05 09:38:35 +02:00
|
|
|
texture->commitTextureOperations( state.rhi(), state.resourceUpdateBatch() );
|
2022-09-28 20:14:10 +02:00
|
|
|
#endif
|
|
|
|
|
2022-10-05 09:38:35 +02:00
|
|
|
textures[0] = texture;
|
2022-09-28 20:14:10 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
class LinearMaterial final : public GradientMaterial
|
|
|
|
{
|
|
|
|
public:
|
2022-09-29 12:40:22 +02:00
|
|
|
LinearMaterial()
|
|
|
|
: GradientMaterial( QGradient::LinearGradient )
|
2022-09-28 20:14:10 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-09-29 12:40:22 +02:00
|
|
|
bool setGradient( const QLinearGradient* gradient )
|
|
|
|
{
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
if ( gradient->stops() != stops() )
|
|
|
|
{
|
|
|
|
setStops( gradient->stops() );
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( gradient->spread() != spread() )
|
|
|
|
{
|
|
|
|
setSpread( gradient->spread() );
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QVector2D start( gradient->start() );
|
|
|
|
const QVector2D stop( gradient->finalStop() );
|
|
|
|
|
|
|
|
if ( m_start != start || m_stop != stop )
|
|
|
|
{
|
|
|
|
m_start = start;
|
|
|
|
m_stop = stop;
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return changed;
|
|
|
|
}
|
2022-09-28 20:14:10 +02:00
|
|
|
|
|
|
|
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 );
|
|
|
|
}
|
|
|
|
|
2022-09-29 12:40:22 +02:00
|
|
|
QSGMaterialShader* createShader() const override;
|
|
|
|
|
|
|
|
QVector2D m_start;
|
|
|
|
QVector2D m_stop;
|
2022-09-28 20:14:10 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
#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:
|
2022-09-29 12:40:22 +02:00
|
|
|
RadialMaterial()
|
|
|
|
: GradientMaterial( QGradient::RadialGradient )
|
2022-09-28 20:14:10 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QSGMaterialType* type() const override
|
|
|
|
{
|
|
|
|
static QSGMaterialType type;
|
|
|
|
return &type;
|
|
|
|
}
|
|
|
|
|
2022-09-29 12:40:22 +02:00
|
|
|
bool setGradient( const QRadialGradient* gradient )
|
|
|
|
{
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
if ( gradient->stops() != stops() )
|
|
|
|
{
|
|
|
|
setStops( gradient->stops() );
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( gradient->spread() != spread() )
|
|
|
|
{
|
|
|
|
setSpread( gradient->spread() );
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
|
2022-10-07 14:32:20 +02:00
|
|
|
const QVector2D focalToCenter( gradient->center() - gradient->focalPoint() );
|
2022-09-29 12:40:22 +02:00
|
|
|
const float centerRadius = gradient->centerRadius();
|
|
|
|
|
|
|
|
const QVector2D focalPoint( gradient->focalPoint() );
|
|
|
|
const float focalRadius = gradient->focalRadius();
|
|
|
|
|
|
|
|
if ( ( focalPoint != m_focalPoint ) || ( focalRadius != m_focalRadius ) )
|
|
|
|
{
|
|
|
|
m_focalPoint = focalPoint;
|
|
|
|
m_focalRadius = focalRadius;
|
|
|
|
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
|
2022-10-07 14:32:20 +02:00
|
|
|
if ( ( focalToCenter != m_focalToCenter ) || ( m_centerRadius != centerRadius ) )
|
|
|
|
{
|
|
|
|
m_focalToCenter = focalToCenter;
|
|
|
|
m_centerRadius = centerRadius;
|
|
|
|
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
|
2022-09-29 12:40:22 +02:00
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
2022-09-28 20:14:10 +02:00
|
|
|
int compare( const QSGMaterial* other ) const override
|
|
|
|
{
|
|
|
|
const auto mat = static_cast< const RadialMaterial* >( other );
|
|
|
|
|
2022-10-07 14:32:20 +02:00
|
|
|
if ( ( m_focalPoint != mat->m_focalPoint )
|
|
|
|
|| ( m_focalToCenter != mat->m_focalToCenter )
|
2022-09-28 20:14:10 +02:00
|
|
|
|| qskFuzzyCompare( m_centerRadius, mat->m_centerRadius )
|
|
|
|
|| qskFuzzyCompare( m_focalRadius, mat->m_focalRadius ) )
|
|
|
|
{
|
|
|
|
return QSGMaterial::compare( other );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return GradientMaterial::compare( other );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-29 12:40:22 +02:00
|
|
|
QSGMaterialShader* createShader() const override;
|
|
|
|
|
|
|
|
QVector2D m_focalPoint;
|
2022-10-07 14:32:20 +02:00
|
|
|
QVector2D m_focalToCenter;
|
2022-09-29 12:40:22 +02:00
|
|
|
float m_centerRadius = 0.0;
|
|
|
|
float m_focalRadius = 0.0;
|
2022-09-28 20:14:10 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef SHADER_GL
|
|
|
|
class RadialShaderGL final : public GradientShaderGL
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
RadialShaderGL()
|
|
|
|
{
|
|
|
|
setShaderFiles( "radialgradient" );
|
|
|
|
}
|
|
|
|
|
|
|
|
void initialize() override
|
|
|
|
{
|
|
|
|
GradientShaderGL::initialize();
|
|
|
|
|
|
|
|
auto p = program();
|
|
|
|
|
2022-10-07 16:17:52 +02:00
|
|
|
m_focalPointId = p->uniformLocation( "translationPoint" );
|
|
|
|
m_focalToCenterId = p->uniformLocation( "focalToCenter" );
|
2022-09-28 20:14:10 +02:00
|
|
|
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();
|
|
|
|
|
2022-10-07 16:17:52 +02:00
|
|
|
p->setUniformValue( m_focalToCenterId, material->m_focalToCenter );
|
2022-09-28 20:14:10 +02:00
|
|
|
p->setUniformValue( m_centerRadiusId, material->m_centerRadius);
|
|
|
|
p->setUniformValue( m_focalRadiusId, material->m_focalRadius);
|
|
|
|
p->setUniformValue( m_focalPointId, material->m_focalPoint);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2022-10-07 16:17:52 +02:00
|
|
|
int m_focalToCenterId = -1;
|
2022-09-28 20:14:10 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-10-07 14:32:20 +02:00
|
|
|
if ( matOld == nullptr || matNew->m_focalToCenter != matOld->m_focalToCenter )
|
2022-09-28 20:14:10 +02:00
|
|
|
{
|
2022-10-07 14:32:20 +02:00
|
|
|
memcpy( data + 72, &matNew->m_focalToCenter, 8 );
|
2022-09-28 20:14:10 +02:00
|
|
|
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 )
|
|
|
|
{
|
2022-10-07 14:32:20 +02:00
|
|
|
memcpy( data + 84, &matNew->m_focalRadius, 4 );
|
2022-09-28 20:14:10 +02:00
|
|
|
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
|
|
|
|
{
|
2022-10-04 12:46:17 +02:00
|
|
|
class ConicMaterial final : public GradientMaterial
|
2022-09-28 20:14:10 +02:00
|
|
|
{
|
|
|
|
public:
|
2022-10-04 12:46:17 +02:00
|
|
|
ConicMaterial()
|
2022-09-29 12:40:22 +02:00
|
|
|
: GradientMaterial( QGradient::ConicalGradient )
|
2022-09-28 20:14:10 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QSGMaterialType* type() const override
|
|
|
|
{
|
|
|
|
static QSGMaterialType type;
|
|
|
|
return &type;
|
|
|
|
}
|
|
|
|
|
2022-09-29 12:40:22 +02:00
|
|
|
bool setGradient( const QConicalGradient* gradient )
|
|
|
|
{
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
if ( gradient->stops() != stops() )
|
|
|
|
{
|
|
|
|
setStops( gradient->stops() );
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QVector2D center( gradient->center() );
|
|
|
|
const float radians = -qDegreesToRadians( gradient->angle() );
|
|
|
|
|
|
|
|
if ( center != m_center || radians != m_radians )
|
|
|
|
{
|
|
|
|
m_center = center;
|
|
|
|
m_radians = radians;
|
|
|
|
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
2022-09-28 20:14:10 +02:00
|
|
|
int compare( const QSGMaterial* other ) const override
|
|
|
|
{
|
2022-10-04 12:46:17 +02:00
|
|
|
const auto mat = static_cast< const ConicMaterial* >( other );
|
2022-09-28 20:14:10 +02:00
|
|
|
|
|
|
|
if ( ( m_center != mat->m_center ) || qskFuzzyCompare( m_radians, mat->m_radians ) )
|
|
|
|
return QSGMaterial::compare( other );
|
|
|
|
else
|
|
|
|
return GradientMaterial::compare( other );
|
|
|
|
}
|
|
|
|
|
2022-09-29 12:40:22 +02:00
|
|
|
QSGMaterialShader* createShader() const override;
|
|
|
|
|
|
|
|
QVector2D m_center;
|
|
|
|
float m_radians = 0.0;
|
2022-09-28 20:14:10 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef SHADER_GL
|
2022-10-04 12:46:17 +02:00
|
|
|
class ConicShaderGL final : public GradientShaderGL
|
2022-09-28 20:14:10 +02:00
|
|
|
{
|
|
|
|
public:
|
2022-10-04 12:46:17 +02:00
|
|
|
ConicShaderGL()
|
2022-09-28 20:14:10 +02:00
|
|
|
{
|
|
|
|
setShaderFiles( "conicalgradient" );
|
|
|
|
}
|
|
|
|
|
|
|
|
void initialize() override
|
|
|
|
{
|
|
|
|
GradientShaderGL::initialize();
|
|
|
|
|
|
|
|
m_radiansId = program()->uniformLocation( "angle" );
|
|
|
|
m_centerPointId = program()->uniformLocation( "translationPoint" );
|
|
|
|
}
|
|
|
|
|
|
|
|
void updateUniformValues( const GradientMaterial* newMaterial ) override
|
|
|
|
{
|
2022-10-04 12:46:17 +02:00
|
|
|
auto material = static_cast< const ConicMaterial* >( newMaterial );
|
2022-09-28 20:14:10 +02:00
|
|
|
|
|
|
|
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
|
2022-10-04 12:46:17 +02:00
|
|
|
class ConicShaderRhi final : public GradientShaderRhi
|
2022-09-28 20:14:10 +02:00
|
|
|
{
|
|
|
|
public:
|
2022-10-04 12:46:17 +02:00
|
|
|
ConicShaderRhi()
|
2022-09-28 20:14:10 +02:00
|
|
|
{
|
|
|
|
setShaderFiles( "conicalgradient" );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool updateUniformData( RenderState& state,
|
|
|
|
QSGMaterial* newMaterial, QSGMaterial* oldMaterial ) override
|
|
|
|
{
|
2022-10-04 12:46:17 +02:00
|
|
|
auto matNew = static_cast< ConicMaterial* >( newMaterial );
|
|
|
|
auto matOld = static_cast< ConicMaterial* >( oldMaterial );
|
2022-09-28 20:14:10 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2022-10-04 12:46:17 +02:00
|
|
|
QSGMaterialShader* ConicMaterial::createShader() const
|
2022-09-28 20:14:10 +02:00
|
|
|
{
|
|
|
|
#ifdef SHADER_GL
|
|
|
|
if ( !( flags() & QSGMaterial::RhiShaderWanted ) )
|
2022-10-04 12:46:17 +02:00
|
|
|
return new ConicShaderGL;
|
2022-09-28 20:14:10 +02:00
|
|
|
#endif
|
2022-10-04 12:46:17 +02:00
|
|
|
return new ConicShaderRhi;
|
2022-09-28 20:14:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-29 12:40:22 +02:00
|
|
|
QskGradientMaterial::QskGradientMaterial( QGradient::Type type )
|
|
|
|
: m_gradientType( type )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
template< typename Material >
|
|
|
|
inline Material* qskEnsureMaterial( QskGradientMaterial* material )
|
|
|
|
{
|
|
|
|
if ( material == nullptr )
|
|
|
|
material = new Material();
|
|
|
|
|
|
|
|
return static_cast< Material* >( material );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QskGradientMaterial::updateGradient( const QGradient* gradient )
|
2022-09-28 20:14:10 +02:00
|
|
|
{
|
2022-09-29 12:40:22 +02:00
|
|
|
Q_ASSERT( gradient && gradient->type() == m_gradientType );
|
|
|
|
|
|
|
|
if ( gradient == nullptr || gradient->type() != m_gradientType )
|
|
|
|
return false;
|
|
|
|
|
2022-09-28 20:14:10 +02:00
|
|
|
switch ( static_cast< int >( gradient->type() ) )
|
|
|
|
{
|
|
|
|
case QGradient::LinearGradient:
|
|
|
|
{
|
2022-09-29 12:40:22 +02:00
|
|
|
auto material = static_cast< LinearMaterial* >( this );
|
|
|
|
return material->setGradient( static_cast< const QLinearGradient* >( gradient ) );
|
2022-09-28 20:14:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
case QGradient::RadialGradient:
|
|
|
|
{
|
2022-09-29 12:40:22 +02:00
|
|
|
auto material = static_cast< RadialMaterial* >( this );
|
|
|
|
return material->setGradient( static_cast< const QRadialGradient* >( gradient ) );
|
2022-09-28 20:14:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
case QGradient::ConicalGradient:
|
|
|
|
{
|
2022-10-04 12:46:17 +02:00
|
|
|
auto material = static_cast< ConicMaterial* >( this );
|
2022-09-29 12:40:22 +02:00
|
|
|
return material->setGradient( static_cast< const QConicalGradient* >( gradient ) );
|
2022-09-28 20:14:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-29 12:40:22 +02:00
|
|
|
return false;
|
2022-09-28 20:14:10 +02:00
|
|
|
}
|
|
|
|
|
2022-09-29 12:40:22 +02:00
|
|
|
QskGradientMaterial* QskGradientMaterial::createMaterial( QGradient::Type gradientType )
|
|
|
|
{
|
|
|
|
switch ( gradientType )
|
|
|
|
{
|
|
|
|
case QGradient::LinearGradient:
|
|
|
|
return new LinearMaterial();
|
2022-09-28 20:14:10 +02:00
|
|
|
|
2022-09-29 12:40:22 +02:00
|
|
|
case QGradient::RadialGradient:
|
|
|
|
return new RadialMaterial();
|
|
|
|
|
|
|
|
case QGradient::ConicalGradient:
|
2022-10-04 12:46:17 +02:00
|
|
|
return new ConicMaterial();
|
2022-09-29 12:40:22 +02:00
|
|
|
|
|
|
|
default:
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|