2022-11-16 16:26:50 +00:00
|
|
|
/******************************************************************************
|
|
|
|
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
2023-04-06 07:23:37 +00:00
|
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
2022-11-16 16:26:50 +00:00
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
#include "QskColorRamp.h"
|
|
|
|
#include "QskRgbValue.h"
|
|
|
|
|
|
|
|
QSK_QT_PRIVATE_BEGIN
|
|
|
|
#include <private/qrhi_p.h>
|
|
|
|
#include <private/qsgplaintexture_p.h>
|
|
|
|
QSK_QT_PRIVATE_END
|
|
|
|
|
|
|
|
#include <qcoreapplication.h>
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
class Texture : public QSGPlainTexture
|
|
|
|
{
|
|
|
|
public:
|
2022-12-22 10:45:23 +00:00
|
|
|
Texture( const QskGradientStops& stops, QskGradient::SpreadMode spreadMode )
|
2022-11-16 16:26:50 +00:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
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 ),
|
|
|
|
*/
|
|
|
|
|
2023-02-04 10:53:23 +00:00
|
|
|
const int size = qBound( 256, 2 * stops.count(), 1024 );
|
|
|
|
setImage( QskRgb::colorTable( size, stops ) );
|
2022-11-16 16:26:50 +00:00
|
|
|
|
2022-12-22 10:45:23 +00:00
|
|
|
const auto wrapMode = this->wrapMode( spreadMode );
|
2022-11-16 16:26:50 +00:00
|
|
|
|
|
|
|
setHorizontalWrapMode( wrapMode );
|
|
|
|
setVerticalWrapMode( wrapMode );
|
|
|
|
|
|
|
|
setFiltering( QSGTexture::Linear );
|
2023-04-04 06:49:11 +00:00
|
|
|
}
|
2022-11-16 16:26:50 +00:00
|
|
|
|
|
|
|
private:
|
2022-12-22 10:45:23 +00:00
|
|
|
static inline QSGTexture::WrapMode wrapMode( QskGradient::SpreadMode spreadMode )
|
2022-11-16 16:26:50 +00:00
|
|
|
{
|
2022-12-22 10:45:23 +00:00
|
|
|
switch ( spreadMode )
|
2022-11-16 16:26:50 +00:00
|
|
|
{
|
2022-12-01 11:03:36 +00:00
|
|
|
case QskGradient::RepeatSpread:
|
2022-11-16 16:26:50 +00:00
|
|
|
return QSGTexture::Repeat;
|
|
|
|
|
2022-12-01 11:03:36 +00:00
|
|
|
case QskGradient::ReflectSpread:
|
2022-11-16 16:26:50 +00:00
|
|
|
return QSGTexture::MirroredRepeat;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return QSGTexture::ClampToEdge;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class HashKey
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
inline bool operator==( const HashKey& other ) const
|
|
|
|
{
|
2022-12-22 10:45:23 +00:00
|
|
|
return rhi == other.rhi && spreadMode == other.spreadMode && stops == other.stops;
|
2022-11-16 16:26:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const void* rhi;
|
|
|
|
const QskGradientStops stops;
|
2022-12-22 10:45:23 +00:00
|
|
|
const QskGradient::SpreadMode spreadMode;
|
2022-11-16 16:26:50 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
inline size_t qHash( const HashKey& key, size_t seed = 0 )
|
|
|
|
{
|
2022-12-22 10:45:23 +00:00
|
|
|
size_t values = seed + key.spreadMode;
|
2022-11-16 16:26:50 +00:00
|
|
|
|
|
|
|
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,
|
2022-12-22 10:45:23 +00:00
|
|
|
const QskGradientStops&, QskGradient::SpreadMode );
|
2022-11-16 16:26:50 +00:00
|
|
|
|
|
|
|
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,
|
2022-12-22 10:45:23 +00:00
|
|
|
const QskGradientStops& stops, QskGradient::SpreadMode spreadMode )
|
2022-11-16 16:26:50 +00:00
|
|
|
{
|
2022-12-22 10:45:23 +00:00
|
|
|
const HashKey key { rhi, stops, spreadMode };
|
2022-11-16 16:26:50 +00:00
|
|
|
|
|
|
|
auto texture = m_hashTable[key];
|
|
|
|
if ( texture == nullptr )
|
|
|
|
{
|
2022-12-22 10:45:23 +00:00
|
|
|
texture = new Texture( stops, spreadMode );
|
2022-11-16 16:26:50 +00:00
|
|
|
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,
|
2022-12-22 10:45:23 +00:00
|
|
|
const QskGradientStops& stops, QskGradient::SpreadMode spreadMode )
|
2022-11-16 16:26:50 +00:00
|
|
|
{
|
|
|
|
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 );
|
|
|
|
}
|
|
|
|
|
2022-12-22 10:45:23 +00:00
|
|
|
return s_cache->texture( rhi, stops, spreadMode );
|
2022-11-16 16:26:50 +00:00
|
|
|
}
|