/****************************************************************************** * 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 ); }