Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qsggradientcache.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include <QtGui/private/qdrawhelper_p.h>
7#include <QtGui/rhi/qrhi.h>
8
9#include <QtQuick/qsgtexture.h>
10#include <QtQuick/private/qsgplaintexture_p.h>
11
13
14static void generateGradientColorTable(const QSGGradientCacheKey &gradient,
15 uint *colorTable, int size, float opacity)
16{
17 int pos = 0;
18 const QGradientStops &s = gradient.stops;
19 Q_ASSERT(!s.isEmpty());
20 const bool colorInterpolation = true;
21
22 uint alpha = qRound(opacity * 256);
23 uint current_color = ARGB_COMBINE_ALPHA(s[0].second.rgba(), alpha);
24 qreal incr = 1.0 / qreal(size);
25 qreal fpos = 1.5 * incr;
26 colorTable[pos++] = ARGB2RGBA(qPremultiply(current_color));
27
28 while (fpos <= s.first().first) {
29 colorTable[pos] = colorTable[pos - 1];
30 pos++;
31 fpos += incr;
32 }
33
34 if (colorInterpolation)
35 current_color = qPremultiply(current_color);
36
37 const int sLast = s.size() - 1;
38 for (int i = 0; i < sLast; ++i) {
39 qreal delta = 1/(s[i+1].first - s[i].first);
40 uint next_color = ARGB_COMBINE_ALPHA(s[i + 1].second.rgba(), alpha);
41 if (colorInterpolation)
42 next_color = qPremultiply(next_color);
43
44 while (fpos < s[i+1].first && pos < size) {
45 int dist = int(256 * ((fpos - s[i].first) * delta));
46 int idist = 256 - dist;
47 if (colorInterpolation)
48 colorTable[pos] = ARGB2RGBA(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
49 else
50 colorTable[pos] = ARGB2RGBA(qPremultiply(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist)));
51 ++pos;
52 fpos += incr;
53 }
54 current_color = next_color;
55 }
56
57 uint last_color = ARGB2RGBA(qPremultiply(ARGB_COMBINE_ALPHA(s[sLast].second.rgba(), alpha)));
58 for ( ; pos < size; ++pos)
59 colorTable[pos] = last_color;
60
61 colorTable[size-1] = last_color;
62}
63
64QSGGradientCache::QSGGradientCache()
65{
66 static int envLimit = qEnvironmentVariableIntValue("QT_QUICKSHAPES_MAX_GRADIENTS");
67 m_cache.setMaxCost(envLimit > 0 ? envLimit - 1 : 255);
68}
69
70QSGGradientCache::~QSGGradientCache()
71{
72 qDeleteAll(m_textures);
73}
74
75QSGGradientCache *QSGGradientCache::cacheForRhi(QRhi *rhi)
76{
77 static QHash<QRhi *, QSGGradientCache *> caches;
78 auto it = caches.constFind(rhi);
79 if (it != caches.constEnd())
80 return *it;
81
82 QSGGradientCache *cache = new QSGGradientCache;
83 rhi->addCleanupCallback([cache](QRhi *rhi) {
84 caches.remove(rhi);
85 delete cache;
86 });
87 caches.insert(rhi, cache);
88 return cache;
89}
90
91QSGTexture *QSGGradientCache::get(const QSGGradientCacheKey &grad)
92{
93 QSGPlainTexture *tx = nullptr;
94
95 // How the cache works:
96 // m_textures is a list of texture pointers.
97 // m_cache holds indexes into the m_textures list.
98 // As long as m_cache is not full, we add new texture pointers to the end of the list.
99 // When m_cache is full, we instead reuse one of the existing texture pointers.
100 // So, the returned texture pointers are never invalidated, although their content may change.
101 //
102 // The trick to find which texture to reuse is the IndexHolder object. When m_cache is full,
103 // each insert() will cause an object to be deleted to make room. The IndexHolder destructor
104 // then stores the index of the cleaned-out object in m_freeIndex. That is then used for the
105 // next insertion.
106
107 const IndexHolder *ih = m_cache[grad];
108 if (ih) {
109 tx = m_textures[ih->idx];
110 } else {
111 qsizetype newIdx = m_freeIndex;
112 if (newIdx < 0) {
113 newIdx = m_textures.size();
114 m_textures.append(new QSGPlainTexture);
115 }
116 tx = m_textures[newIdx];
117 setTextureData(tx, grad);
118 m_cache.insert(grad, new IndexHolder{newIdx, &m_freeIndex});
119 }
120 return tx;
121}
122
123void QSGGradientCache::setTextureData(QSGPlainTexture *tx, const QSGGradientCacheKey &grad)
124{
125 static const int W = 1024; // texture size is 1024x1
126 QImage gradTab(W, 1, QImage::Format_RGBA8888_Premultiplied);
127 if (!grad.stops.isEmpty())
128 generateGradientColorTable(grad, reinterpret_cast<uint *>(gradTab.bits()), W, 1.0f);
129 else
130 gradTab.fill(Qt::black);
131 tx->setImage(gradTab);
132 switch (grad.spread) {
133 case QGradient::PadSpread:
134 tx->setHorizontalWrapMode(QSGTexture::ClampToEdge);
135 tx->setVerticalWrapMode(QSGTexture::ClampToEdge);
136 break;
137 case QGradient::RepeatSpread:
138 tx->setHorizontalWrapMode(QSGTexture::Repeat);
139 tx->setVerticalWrapMode(QSGTexture::Repeat);
140 break;
141 case QGradient::ReflectSpread:
142 tx->setHorizontalWrapMode(QSGTexture::MirroredRepeat);
143 tx->setVerticalWrapMode(QSGTexture::MirroredRepeat);
144 break;
145 default:
146 qWarning("Unknown gradient spread mode %d", grad.spread);
147 break;
148 }
149 tx->setFiltering(QSGTexture::Linear);
150}
151
152QT_END_NAMESPACE
Combined button and popup list for selecting options.
static QT_BEGIN_NAMESPACE void generateGradientColorTable(const QSGGradientCacheKey &gradient, uint *colorTable, int size, float opacity)