QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskColorRamp.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskColorRamp.h"
7#include "QskRgbValue.h"
8
9QSK_QT_PRIVATE_BEGIN
10#include <private/qrhi_p.h>
11#include <private/qsgplaintexture_p.h>
12QSK_QT_PRIVATE_END
13
14#include <qcoreapplication.h>
15
16namespace
17{
18 class Texture : public QSGPlainTexture
19 {
20 public:
21 Texture( const QskGradientStops& stops, QskGradient::SpreadMode spreadMode )
22 {
23 /*
24 Qt creates tables of 1024 colors, while Chrome, Firefox, and Android
25 seem to use 256 colors only ( according to maybe outdated sources
26 from the internet ),
27 */
28
29 const int size = qBound( 256, 2 * stops.count(), 1024 );
30 setImage( QskRgb::colorTable( size, stops ) );
31
32 const auto wrapMode = this->wrapMode( spreadMode );
33
34 setHorizontalWrapMode( wrapMode );
35 setVerticalWrapMode( wrapMode );
36
37 setFiltering( QSGTexture::Linear );
38 }
39
40 private:
41 static inline QSGTexture::WrapMode wrapMode( QskGradient::SpreadMode spreadMode )
42 {
43 switch ( spreadMode )
44 {
45 case QskGradient::RepeatSpread:
46 return QSGTexture::Repeat;
47
48 case QskGradient::ReflectSpread:
49 return QSGTexture::MirroredRepeat;
50
51 default:
52 return QSGTexture::ClampToEdge;
53 }
54 }
55 };
56
57 class HashKey
58 {
59 public:
60 inline bool operator==( const HashKey& other ) const
61 {
62 return rhi == other.rhi && spreadMode == other.spreadMode && stops == other.stops;
63 }
64
65 const void* rhi;
66 const QskGradientStops stops;
67 const QskGradient::SpreadMode spreadMode;
68 };
69
70 inline size_t qHash( const HashKey& key, size_t seed = 0 )
71 {
72 size_t values = seed + key.spreadMode;
73
74 for ( const auto& stop : key.stops )
75 values += stop.rgb();
76
77 return values;
78 }
79
80 class Cache
81 {
82 public:
83 ~Cache() { qDeleteAll( m_hashTable ); }
84
85 void cleanupRhi( const QRhi* );
86
87 Texture* texture( const void* rhi,
88 const QskGradientStops&, QskGradient::SpreadMode );
89
90 private:
91 QHash< HashKey, Texture* > m_hashTable;
92 QVector< const QRhi* > m_rhiTable; // no QSet: we usually have only one entry
93 };
94
95 static Cache* s_cache;
96}
97
98static void qskCleanupCache()
99{
100 delete s_cache;
101 s_cache = nullptr;
102}
103
104static void qskCleanupRhi( const QRhi* rhi )
105{
106 if ( s_cache )
107 s_cache->cleanupRhi( rhi );
108}
109
110Texture* Cache::texture( const void* rhi,
111 const QskGradientStops& stops, QskGradient::SpreadMode spreadMode )
112{
113 const HashKey key { rhi, stops, spreadMode };
114
115 auto texture = m_hashTable[key];
116 if ( texture == nullptr )
117 {
118 texture = new Texture( stops, spreadMode );
119 m_hashTable[ key ] = texture;
120
121 if ( rhi != nullptr )
122 {
123 auto myrhi = ( QRhi* )rhi;
124
125 if ( !m_rhiTable.contains( myrhi ) )
126 {
127 myrhi->addCleanupCallback( qskCleanupRhi );
128 m_rhiTable += myrhi;
129 }
130 }
131 }
132
133 return texture;
134}
135
136void Cache::cleanupRhi( const QRhi* rhi )
137{
138 for ( auto it = m_hashTable.begin(); it != m_hashTable.end(); )
139 {
140 if ( it.key().rhi == rhi )
141 {
142 delete it.value();
143 it = m_hashTable.erase( it );
144 }
145 else
146 {
147 ++it;
148 }
149 }
150
151 m_rhiTable.removeAll( rhi );
152}
153
154QSGTexture* QskColorRamp::texture( const void* rhi,
155 const QskGradientStops& stops, QskGradient::SpreadMode spreadMode )
156{
157 if ( s_cache == nullptr )
158 {
159 s_cache = new Cache();
160
161 /*
162 For RHI we have QRhi::addCleanupCallback, but with
163 OpenGL we would have to fiddle around with QOpenGLSharedResource
164 But as the OpenGL path is only for Qt5 we do not want to spend
165 much energy on finetuning the resource management.
166 */
167 qAddPostRoutine( qskCleanupCache );
168 }
169
170 return s_cache->texture( rhi, stops, spreadMode );
171}