diff --git a/src/nodes/QskColorRamp.cpp b/src/nodes/QskColorRamp.cpp new file mode 100644 index 00000000..49f421e8 --- /dev/null +++ b/src/nodes/QskColorRamp.cpp @@ -0,0 +1,170 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#include "QskColorRamp.h" +#include "QskRgbValue.h" + +QSK_QT_PRIVATE_BEGIN +#include +#include +QSK_QT_PRIVATE_END + +#include + +namespace +{ + class Texture : public QSGPlainTexture + { + public: + Texture( const QskGradientStops& stops, QGradient::Spread spread ) + { + /* + Qt creates tables of 1024 colors, while Chrome, Firefox, and Android + seem to use 256 colors only ( according to maybe outdated sources + from the internet ), + */ + + setImage( QskRgb::colorTable( 256, 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 HashKey + { + public: + inline bool operator==( const HashKey& other ) const + { + return rhi == other.rhi && spread == other.spread && stops == other.stops; + } + + const void* rhi; + const QskGradientStops stops; + const QGradient::Spread spread; + }; + + inline size_t qHash( const HashKey& key, size_t seed = 0 ) + { + size_t values = seed + key.spread; + + for ( const auto& stop : key.stops ) + values += stop.rgb(); + + return values; + } + + class Cache + { + public: + ~Cache() { qDeleteAll( m_hashTable ); } + + void cleanupRhi( const QRhi* ); + + Texture* texture( const void* rhi, + const QskGradientStops&, QGradient::Spread ); + + private: + QHash< HashKey, Texture* > m_hashTable; + QVector< const QRhi* > m_rhiTable; // no QSet: we usually have only one entry + }; + + static Cache* s_cache; +} + +static void qskCleanupCache() +{ + delete s_cache; + s_cache = nullptr; +} + +static void qskCleanupRhi( const QRhi* rhi ) +{ + if ( s_cache ) + s_cache->cleanupRhi( rhi ); +} + +Texture* Cache::texture( const void* rhi, + const QskGradientStops& stops, QGradient::Spread spread ) +{ + const HashKey key { rhi, stops, spread }; + + auto texture = m_hashTable[key]; + if ( texture == nullptr ) + { + texture = new Texture( stops, spread ); + m_hashTable[ key ] = texture; + + if ( rhi != nullptr ) + { + auto myrhi = ( QRhi* )rhi; + + if ( !m_rhiTable.contains( myrhi ) ) + { + myrhi->addCleanupCallback( qskCleanupRhi ); + m_rhiTable += myrhi; + } + } + } + + return texture; +} + +void Cache::cleanupRhi( const QRhi* rhi ) +{ + for ( auto it = m_hashTable.begin(); it != m_hashTable.end(); ) + { + if ( it.key().rhi == rhi ) + { + delete it.value(); + it = m_hashTable.erase( it ); + } + else + { + ++it; + } + } + + m_rhiTable.removeAll( rhi ); +} + +QSGTexture* QskColorRamp::texture( const void* rhi, + const QskGradientStops& stops, QGradient::Spread spread ) +{ + if ( s_cache == nullptr ) + { + s_cache = new Cache(); + + /* + For RHI we have QRhi::addCleanupCallback, but with + OpenGL we would have to fiddle around with QOpenGLSharedResource + But as the OpenGL path is only for Qt5 we do not want to spend + much energy on finetuning the resource management. + */ + qAddPostRoutine( qskCleanupCache ); + } + + return s_cache->texture( rhi, stops, spread ); +} diff --git a/src/nodes/QskColorRamp.h b/src/nodes/QskColorRamp.h new file mode 100644 index 00000000..41d173f7 --- /dev/null +++ b/src/nodes/QskColorRamp.h @@ -0,0 +1,21 @@ +/********************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#ifndef QSK_COLOR_RAMP_H +#define QSK_COLOR_RAMP_H + +#include "QskGlobal.h" +#include "QskGradientStop.h" + +#include + +class QSGTexture; + +namespace QskColorRamp +{ + QSGTexture* texture( const void* rhi, const QskGradientStops&, QGradient::Spread ); +} + +#endif diff --git a/src/nodes/QskGradientMaterial.cpp b/src/nodes/QskGradientMaterial.cpp index 6b3bfeeb..f80bf783 100644 --- a/src/nodes/QskGradientMaterial.cpp +++ b/src/nodes/QskGradientMaterial.cpp @@ -7,14 +7,15 @@ #include "QskFunctions.h" #include "QskRgbValue.h" #include "QskGradientDirection.h" +#include "QskColorRamp.h" + +#include QSK_QT_PRIVATE_BEGIN #include #include -#include QSK_QT_PRIVATE_END -#include #include // RHI shaders are supported by Qt 5.15 and Qt 6.x @@ -32,153 +33,6 @@ QSK_QT_PRIVATE_END using RhiShader = QSGMaterialShader; #endif -namespace -{ - class ColorRamp : public QSGPlainTexture - { - public: - ColorRamp( const QskGradientStops& stops, QGradient::Spread spread ) - { - /* - Qt creates tables of 1024 colors, while Chrome, Firefox, and Android - seem to use 256 colors only ( according to maybe outdated sources - from the internet ), - */ - - setImage( QskRgb::colorTable( 256, 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 ColorRampHashKey - { - public: - inline bool operator==( const ColorRampHashKey& other ) const - { - return rhi == other.rhi && spread == other.spread && stops == other.stops; - } - - const void* rhi; - const QskGradientStops stops; - const QGradient::Spread spread; - }; - - inline size_t qHash( const ColorRampHashKey& key, size_t seed = 0 ) - { - size_t valus = seed + key.spread; - - for ( const auto& stop : key.stops ) - valus += stop.rgb(); - - return valus; - } - - class ColorRampCache - { - public: - static ColorRampCache* instance() - { - static ColorRampCache* s_instance = nullptr; - if ( s_instance == nullptr ) - { - s_instance = new ColorRampCache(); - - /* - For RHI we have QRhi::addCleanupCallback, but with - OpenGL we have to fiddle around with QOpenGLSharedResource - So let's keep things simple for the moment. TODO ... - */ - qAddPostRoutine( cleanup ); - } - - return s_instance; - } - - ~ColorRampCache() - { - qDeleteAll( m_hashTable ); - } - - ColorRamp* colorRamp( const void* rhi, - const QskGradientStops& stops, QGradient::Spread spread ) - { - const ColorRampHashKey key { rhi, stops, spread }; - - auto texture = m_hashTable[key]; - if ( texture == nullptr ) - { - texture = new ColorRamp( stops, spread ); - m_hashTable[ key ] = texture; - - if ( rhi != nullptr ) - { - auto myrhi = ( QRhi* )rhi; - - if ( !m_rhiTable.contains( myrhi ) ) - { - myrhi->addCleanupCallback( ColorRampCache::cleanupRhi ); - m_rhiTable += myrhi; - } - } - } - - return texture; - } - - private: - static void cleanup() - { - delete instance(); - } - - static void cleanupRhi( const QRhi *rhi ) - { - auto cache = instance(); - - auto& table = cache->m_hashTable; - for ( auto it = table.begin(); it != table.end(); ) - { - if ( it.key().rhi == rhi ) - { - delete it.value(); - it = table.erase( it ); - } - else - { - ++it; - } - } - - cache->m_rhiTable.removeAll( rhi ); - } - - QHash< ColorRampHashKey, ColorRamp* > m_hashTable; - QVector< const QRhi* > m_rhiTable; - }; -} - namespace { class GradientMaterial : public QskGradientMaterial @@ -215,6 +69,8 @@ namespace virtual QSGMaterialShader* createShader() const = 0; #endif + + virtual bool setGradient( const QRectF&, const QskGradient& ) = 0; }; #ifdef SHADER_GL @@ -250,9 +106,9 @@ namespace updateUniformValues( material ); - auto colorRamp = ColorRampCache::instance()->colorRamp( + auto texture = QskColorRamp::texture( nullptr, material->stops(), material->spread() ); - colorRamp->bind(); + texture->bind(); } char const* const* attributeNames() const override final @@ -282,23 +138,23 @@ namespace } void updateSampledImage( RenderState& state, int binding, - QSGTexture* textures[], QSGMaterial* newMaterial, QSGMaterial*) override final + QSGTexture* textures[], QSGMaterial* newMaterial, QSGMaterial* ) override final { if ( binding != 1 ) return; auto material = static_cast< const GradientMaterial* >( newMaterial ); - auto colorRamp = ColorRampCache::instance()->colorRamp( + auto texture = QskColorRamp::texture( state.rhi(), material->stops(), material->spread() ); #if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) - colorRamp->updateRhiTexture( state.rhi(), state.resourceUpdateBatch() ); + texture->updateRhiTexture( state.rhi(), state.resourceUpdateBatch() ); #else - colorRamp->commitTextureOperations( state.rhi(), state.resourceUpdateBatch() ); + texture->commitTextureOperations( state.rhi(), state.resourceUpdateBatch() ); #endif - textures[0] = colorRamp; + textures[0] = texture; } }; #endif @@ -314,7 +170,7 @@ namespace { } - bool setGradient( const QRectF& rect, const QskGradient& gradient ) + bool setGradient( const QRectF& rect, const QskGradient& gradient ) override { bool changed = false; @@ -475,7 +331,7 @@ namespace return &type; } - bool setGradient( const QRectF& rect, const QskGradient& gradient ) + bool setGradient( const QRectF& rect, const QskGradient& gradient ) override { bool changed = false; @@ -646,7 +502,7 @@ namespace return &type; } - bool setGradient( const QRectF& rect, const QskGradient& gradient ) + bool setGradient( const QRectF& rect, const QskGradient& gradient ) override { bool changed = false; @@ -835,33 +691,20 @@ bool QskGradientMaterial::updateGradient( const QRectF& rect, const QskGradient& { Q_ASSERT( gradient.type() == m_gradientType ); - if ( gradient.type() != m_gradientType ) - return false; - - switch ( gradient.type() ) + if ( gradient.type() == m_gradientType ) { - case QskGradient::Linear: + switch ( gradient.type() ) { - auto material = static_cast< LinearMaterial* >( this ); - return material->setGradient( rect, gradient ); - } + case QskGradient::Linear: + case QskGradient::Radial: + case QskGradient::Conic: + { + auto material = static_cast< GradientMaterial* >( this ); + return material->setGradient( rect, gradient ); + } - case QskGradient::Radial: - { - auto material = static_cast< RadialMaterial* >( this ); - return material->setGradient( rect, gradient ); - } - - case QskGradient::Conic: - { - auto material = static_cast< ConicMaterial* >( this ); - return material->setGradient( rect, gradient ); - } - - default: - { - qWarning( "Invalid gradient type" ); - break; + default: + qWarning( "Invalid gradient type" ); } } diff --git a/src/src.pro b/src/src.pro index 0005329e..02a76ae2 100644 --- a/src/src.pro +++ b/src/src.pro @@ -106,6 +106,7 @@ HEADERS += \ nodes/QskBoxRenderer.h \ nodes/QskBoxRendererColorMap.h \ nodes/QskBoxShadowNode.h \ + nodes/QskColorRamp.h \ nodes/QskGraphicNode.h \ nodes/QskPaintedNode.h \ nodes/QskPlainTextRenderer.h \ @@ -131,6 +132,7 @@ SOURCES += \ nodes/QskBoxRendererEllipse.cpp \ nodes/QskBoxRendererDEllipse.cpp \ nodes/QskBoxShadowNode.cpp \ + nodes/QskColorRamp.cpp \ nodes/QskGraphicNode.cpp \ nodes/QskPaintedNode.cpp \ nodes/QskPlainTextRenderer.cpp \