Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
Loading...
Searching...
No Matches
qpixmapcache.cpp
Go to the documentation of this file.
1
// Copyright (C) 2016 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
// Qt-Security score:significant reason:default
4
5
#
include
"qpixmapcache.h"
6
#
include
"qbasictimer.h"
7
#
include
"qobject.h"
8
#
include
"qdebug.h"
9
#
include
"qpixmapcache_p.h"
10
#
include
"qthread.h"
11
#
include
"qcoreapplication.h"
12
13
using
namespace
std::chrono_literals;
14
15
QT_BEGIN_NAMESPACE
16
17
/*!
18
\class QPixmapCache
19
\inmodule QtGui
20
21
\brief The QPixmapCache class provides an application-wide cache for pixmaps.
22
23
This class is a tool for optimized drawing with QPixmap. You can
24
use it to store temporary pixmaps that are expensive to generate
25
without using more storage space than cacheLimit(). Use insert()
26
to insert pixmaps, find() to find them, and clear() to empty the
27
cache.
28
29
QPixmapCache contains no member data, only static functions to
30
access the global pixmap cache. It creates an internal QCache
31
object for caching the pixmaps.
32
33
The cache associates a pixmap with a user-provided string as a key,
34
or with a QPixmapCache::Key that the cache generates.
35
Using QPixmapCache::Key for keys is faster than using strings. The string API is
36
very convenient for complex keys but the QPixmapCache::Key API will be very
37
efficient and convenient for a one-to-one object-to-pixmap mapping - in
38
this case, you can store the keys as members of an object.
39
40
If two pixmaps are inserted into the cache using equal keys then the
41
last pixmap will replace the first pixmap in the cache. This follows the
42
behavior of the QHash and QCache classes.
43
44
The cache becomes full when the total size of all pixmaps in the
45
cache exceeds cacheLimit(). The initial cache limit is 10240 KB (10 MB);
46
you can change this by calling setCacheLimit() with the required value.
47
A pixmap takes roughly (\e{width} * \e{height} * \e{depth})/8 bytes of
48
memory.
49
50
The \e{Qt Quarterly} article
51
\l{http://doc.qt.io/archives/qq/qq12-qpixmapcache.html}{Optimizing
52
with QPixmapCache} explains how to use QPixmapCache to speed up
53
applications by caching the results of painting.
54
55
\note QPixmapCache is only usable from the application's main thread.
56
Access from other threads will be ignored and return failure.
57
58
\sa QCache, QPixmap
59
*/
60
61
static
const
int
cache_limit_default
= 10240;
// 10 MB cache limit
62
63
static
inline
qsizetype
cost
(
const
QPixmap &pixmap)
64
{
65
// make sure to do a 64bit calculation; qsizetype might be smaller
66
const
qint64 costKb =
static_cast
<qint64>(pixmap.width())
67
* pixmap.height() * pixmap.depth() / (8 * 1024);
68
const
qint64 costMax = std::numeric_limits<qsizetype>::max();
69
// a small pixmap should have at least a cost of 1(kb)
70
return
static_cast
<qsizetype>(qBound(1LL, costKb, costMax));
71
}
72
73
static
inline
bool
qt_pixmapcache_thread_test
()
74
{
75
if
(Q_LIKELY(QThread::isMainThread()))
76
return
true
;
77
78
return
false
;
79
}
80
81
/*!
82
\class QPixmapCache::Key
83
\brief The QPixmapCache::Key class can be used for efficient access
84
to the QPixmapCache.
85
\inmodule QtGui
86
87
Use QPixmapCache::insert() to receive an instance of Key generated
88
by the pixmap cache. You can store the key in your own objects for
89
a very efficient one-to-one object-to-pixmap mapping.
90
*/
91
92
/*!
93
Constructs an empty Key object.
94
*/
95
QPixmapCache::Key::Key() : d(
nullptr
)
96
{
97
}
98
99
/*!
100
\internal
101
Constructs a copy of \a other.
102
*/
103
QPixmapCache::Key::Key(
const
Key &other)
104
{
105
if
(other.d)
106
++(other.d->ref);
107
d = other.d;
108
}
109
110
/*!
111
Destroys the key.
112
*/
113
QPixmapCache::Key::~Key()
114
{
115
if
(d && --(d->ref) == 0)
116
delete
d;
117
}
118
119
/*!
120
\internal
121
122
Returns \c true if this key is the same as the given \a key; otherwise returns
123
false.
124
*/
125
bool
QPixmapCache::Key::operator ==(
const
Key &key)
const
126
{
127
return
(d == key.d);
128
}
129
130
/*!
131
\fn bool QPixmapCache::Key::operator !=(const Key &key) const
132
\internal
133
*/
134
135
/*!
136
\fn QPixmapCache::Key::Key(Key &&)
137
\internal
138
\since 5.6
139
*/
140
141
/*!
142
\fn QPixmapCache::Key &QPixmapCache::Key::operator=(Key &&)
143
\internal
144
\since 5.6
145
*/
146
147
/*!
148
\fn void QPixmapCache::Key::swap(Key &)
149
\since 5.6
150
\memberswap{key}
151
*/
152
153
/*!
154
Returns \c true if there is a cached pixmap associated with this key.
155
Otherwise, if pixmap was flushed, the key is no longer valid.
156
\since 5.7
157
*/
158
bool
QPixmapCache::Key::isValid()
const
noexcept
159
{
160
return
d && d->isValid;
161
}
162
163
/*!
164
\internal
165
*/
166
QPixmapCache::Key &QPixmapCache::Key::operator =(
const
Key &other)
167
{
168
if
(d != other.d) {
169
if
(other.d)
170
++(other.d->ref);
171
if
(d && --(d->ref) == 0)
172
delete
d;
173
d = other.d;
174
}
175
return
*
this
;
176
}
177
178
class
QPMCache
:
public
QObject
,
public
QCache
<
QPixmapCache
::
Key
,
QPixmapCacheEntry
>
179
{
180
Q_OBJECT
181
public
:
182
QPMCache
();
183
~
QPMCache
();
184
185
void
timerEvent
(QTimerEvent *)
override
;
186
bool
insert
(
const
QString& key,
const
QPixmap &pixmap,
int
cost);
187
QPixmapCache
::
Key
insert
(
const
QPixmap &pixmap,
int
cost);
188
bool
remove
(
const
QString &key);
189
bool
remove
(
const
QPixmapCache::Key &key);
190
191
void
resizeKeyArray
(
int
size);
192
QPixmapCache
::
Key
createKey
();
193
void
releaseKey
(
const
QPixmapCache::Key &key);
194
void
clear
();
195
196
QPixmap
*
object
(
const
QString &key)
const
;
197
QPixmap
*
object
(
const
QPixmapCache::Key &key)
const
;
198
199
static
inline
QPixmapCache
::
KeyData
*
get
(
const
QPixmapCache::Key &key)
200
{
return
key.d;}
201
202
static
QPixmapCache
::
KeyData
*
getKeyData
(QPixmapCache::Key *key);
203
204
bool
flushDetachedPixmaps
(
bool
nt);
205
206
private
:
207
static
constexpr
auto
soon_time = 10s;
208
static
constexpr
auto
flush_time = 30s;
209
int
*keyArray;
210
QBasicTimer timer;
211
int
ps;
212
int
keyArraySize;
213
int
freeKey;
214
QHash<QString, QPixmapCache::Key> cacheKeys;
215
bool
t;
216
};
217
218
QT_BEGIN_INCLUDE_NAMESPACE
219
#
include
"qpixmapcache.moc"
220
QT_END_INCLUDE_NAMESPACE
221
222
/*!
223
\fn size_t QPixmapCache::Key::qHash(const Key &key, size_t seed)
224
\since 6.6
225
\qhash{QPixmapCache::Key}
226
*/
227
size_t QPixmapCache::Key::hash(size_t seed)
const
noexcept
228
{
229
return
qHash(
this
->d ?
this
->d->key : 0, seed);
230
}
231
232
QPMCache
::QPMCache()
233
: QObject(
nullptr
),
234
QCache<QPixmapCache::Key, QPixmapCacheEntry>(cache_limit_default),
235
keyArray(
nullptr
), ps(0), keyArraySize(0), freeKey(0), t(
false
)
236
{
237
}
238
QPMCache
::~
QPMCache
()
239
{
240
clear
(
)
;
241
free(keyArray);
242
}
243
244
/*
245
This is supposed to cut the cache size down by about 25% in a
246
minute once the application becomes idle, to let any inserted pixmap
247
remain in the cache for some time before it becomes a candidate for
248
cleaning-up, and to not cut down the size of the cache while the
249
cache is in active use.
250
251
When the last detached pixmap has been deleted from the cache, kill the
252
timer so Qt won't keep the CPU from going into sleep mode. Currently
253
the timer is not restarted when the pixmap becomes unused, but it does
254
restart once something else is added (i.e. the cache space is actually needed).
255
256
Returns \c true if any were removed.
257
*/
258
bool
QPMCache
::
flushDetachedPixmaps
(
bool
nt)
259
{
260
auto
mc = maxCost();
261
const
qsizetype currentTotal = totalCost();
262
const
qsizetype oldSize = size();
263
if
(currentTotal)
264
setMaxCost(nt ? currentTotal * 3 / 4 : currentTotal - 1);
265
setMaxCost(mc);
266
ps = totalCost();
267
return
size() != oldSize;
268
}
269
270
void
QPMCache
::
timerEvent
(QTimerEvent *)
271
{
272
bool
nt = totalCost() == ps;
273
if
(!
flushDetachedPixmaps
(
nt
)
) {
274
timer.stop();
275
}
else
if
(nt != t) {
276
timer.start(nt ? soon_time : flush_time,
this
);
277
t = nt;
278
}
279
}
280
281
282
QPixmap
*
QPMCache
::
object
(
const
QString &key)
const
283
{
284
if
(
const
auto
it = cacheKeys.find(key); it != cacheKeys.cend())
285
return
object(it.value());
286
return
nullptr
;
287
}
288
289
QPixmap
*
QPMCache
::
object
(
const
QPixmapCache::Key &key)
const
290
{
291
Q_ASSERT(key.isValid());
292
QPixmap *ptr = QCache<QPixmapCache::Key, QPixmapCacheEntry>::object(key);
293
//We didn't find the pixmap in the cache, the key is not valid anymore
294
if
(!ptr)
295
const_cast
<
QPMCache
*>(
this
)->releaseKey(key);
296
return
ptr;
297
}
298
299
bool
QPMCache
::
insert
(
const
QString& key,
const
QPixmap &pixmap,
int
cost)
300
{
301
//If for the same key we add already a pixmap we should delete it
302
remove(key);
303
304
// this will create a new key; the old one has been removed
305
auto
k = insert(pixmap, cost);
306
if
(k.isValid()) {
307
k.d->stringKey = key;
308
cacheKeys[key] = std::move(k);
309
return
true
;
310
}
311
return
false
;
312
}
313
314
QPixmapCache
::
Key
QPMCache
::
insert
(
const
QPixmap &pixmap,
int
cost)
315
{
316
QPixmapCache::Key cacheKey = createKey();
// invalidated by ~QPixmapCacheEntry on failed insert
317
bool
success = QCache<QPixmapCache::Key, QPixmapCacheEntry>::insert(cacheKey,
new
QPixmapCacheEntry(cacheKey, pixmap), cost);
318
Q_ASSERT(success || !cacheKey.isValid());
319
if
(success) {
320
if
(!timer.isActive()) {
321
timer.start(flush_time,
this
);
322
t =
false
;
323
}
324
}
325
return
cacheKey;
326
}
327
328
bool
QPMCache
::
remove
(
const
QString &key)
329
{
330
const
auto
cacheKey = cacheKeys.take(key);
331
return
cacheKey.isValid() && remove(cacheKey);
332
}
333
334
bool
QPMCache
::
remove
(
const
QPixmapCache::Key &key)
335
{
336
return
QCache<QPixmapCache::Key, QPixmapCacheEntry>::remove(key);
337
}
338
339
void
QPMCache
::
resizeKeyArray
(
int
size)
340
{
341
if
(size <= keyArraySize || size == 0)
342
return
;
343
keyArray = q_check_ptr(
static_cast
<
int
*>(realloc(keyArray,
344
size *
sizeof
(
int
))));
345
for
(
int
i = keyArraySize; i != size; ++i)
346
keyArray[i] = i + 1;
347
keyArraySize = size;
348
}
349
350
QPixmapCache
::
Key
QPMCache
::
createKey
()
351
{
352
if
(freeKey == keyArraySize)
353
resizeKeyArray
(
keyArraySize ? keyArraySize << 1 : 2
)
;
354
int
id = freeKey;
355
freeKey = keyArray[id];
356
QPixmapCache::Key key;
357
QPixmapCache::KeyData *d = QPMCache::getKeyData(&key);
358
d->key = ++id;
359
return
key;
360
}
361
362
void
QPMCache
::
releaseKey
(
const
QPixmapCache::Key &key)
363
{
364
QPixmapCache::KeyData *keyData = key.d;
365
if
(!keyData)
366
return
;
367
if
(!keyData->stringKey.isNull())
368
cacheKeys.remove(keyData->stringKey);
369
if
(keyData->key > keyArraySize || keyData->key <= 0)
370
return
;
371
keyData->key--;
372
keyArray[keyData->key] = freeKey;
373
freeKey = keyData->key;
374
keyData->isValid =
false
;
375
keyData->key = 0;
376
}
377
378
void
QPMCache
::
clear
()
379
{
380
free(keyArray);
381
keyArray =
nullptr
;
382
freeKey = 0;
383
keyArraySize = 0;
384
//Mark all keys as invalid
385
const
QList<QPixmapCache::Key> keys = QCache<QPixmapCache::Key, QPixmapCacheEntry>::keys();
386
for
(
const
auto
&key : keys) {
387
if
(key.d)
388
key.d->isValid =
false
;
389
}
390
QCache<QPixmapCache::Key, QPixmapCacheEntry>::clear();
391
// Nothing left to flush; stop the timer
392
timer.stop();
393
}
394
395
QPixmapCache::KeyData*
QPMCache
::getKeyData(QPixmapCache::Key *key)
396
{
397
if
(!key->d)
398
key->d =
new
QPixmapCache::KeyData;
399
return
key->d;
400
}
401
402
Q_GLOBAL_STATIC(
QPMCache
, pm_cache)
403
404
int
Q_AUTOTEST_EXPORT q_QPixmapCache_keyHashSize()
405
{
406
return
pm_cache()->size();
407
}
408
409
QPixmapCacheEntry
::~
QPixmapCacheEntry
()
410
{
411
pm_cache()->releaseKey(key);
412
}
413
414
/*!
415
Looks for a cached pixmap associated with the given \a key in the cache.
416
If the pixmap is found, the function sets \a pixmap to that pixmap and
417
returns \c true; otherwise it leaves \a pixmap alone and returns \c false.
418
419
Example:
420
\snippet code/src_gui_image_qpixmapcache.cpp 1
421
*/
422
423
bool
QPixmapCache
::find(
const
QString &key, QPixmap *pixmap)
424
{
425
if
(key.isEmpty() || !
qt_pixmapcache_thread_test
(
)
)
426
return
false
;
427
QPixmap *ptr = pm_cache()->object(key);
428
if
(ptr && pixmap)
429
*pixmap = *ptr;
430
return
ptr !=
nullptr
;
431
}
432
433
/*!
434
Looks for a cached pixmap associated with the given \a key in the cache.
435
If the pixmap is found, the function sets \a pixmap to that pixmap and
436
returns \c true; otherwise it leaves \a pixmap alone and returns \c false. If
437
the pixmap is not found, it means that the \a key is no longer valid,
438
so it will be released for the next insertion.
439
*/
440
bool
QPixmapCache
::find(
const
Key &key, QPixmap *pixmap)
441
{
442
if
(!
qt_pixmapcache_thread_test
(
)
)
443
return
false
;
444
//The key is not valid anymore, a flush happened before probably
445
if
(!key.d || !key.d->isValid)
446
return
false
;
447
QPixmap *ptr = pm_cache()->object(key);
448
if
(ptr && pixmap)
449
*pixmap = *ptr;
450
return
ptr !=
nullptr
;
451
}
452
453
/*!
454
Inserts a copy of the pixmap \a pixmap associated with the \a key into
455
the cache.
456
457
All pixmaps inserted by the Qt library have a key starting with
458
"$qt", so your own pixmap keys should never begin "$qt".
459
460
When a pixmap is inserted and the cache is about to exceed its
461
limit, it removes pixmaps until there is enough room for the
462
pixmap to be inserted.
463
464
The oldest pixmaps (least recently accessed in the cache) are
465
deleted when more space is needed.
466
467
The function returns \c true if the object was inserted into the
468
cache; otherwise it returns \c false.
469
470
\sa setCacheLimit()
471
*/
472
473
bool
QPixmapCache
::insert(
const
QString &key,
const
QPixmap &pixmap)
474
{
475
if
(key.isEmpty() || !
qt_pixmapcache_thread_test
(
)
)
476
return
false
;
477
return
pm_cache()->insert(key, pixmap, cost(pixmap));
478
}
479
480
/*!
481
Inserts a copy of the given \a pixmap into the cache and returns a key
482
that can be used to retrieve it.
483
484
When a pixmap is inserted and the cache is about to exceed its
485
limit, it removes pixmaps until there is enough room for the
486
pixmap to be inserted.
487
488
The oldest pixmaps (least recently accessed in the cache) are
489
deleted when more space is needed.
490
491
\sa setCacheLimit()
492
*/
493
QPixmapCache::Key
QPixmapCache
::insert(
const
QPixmap &pixmap)
494
{
495
if
(!qt_pixmapcache_thread_test())
496
return
QPixmapCache::Key();
497
return
pm_cache()->insert(pixmap, cost(pixmap));
498
}
499
500
#
if
QT_DEPRECATED_SINCE
(
6
,
6
)
501
/*!
502
\fn bool QPixmapCache::replace(const Key &key, const QPixmap &pixmap)
503
504
\deprecated [6.6] Use \c{remove(key); key = insert(pixmap);} instead.
505
506
Replaces the pixmap associated with the given \a key with the \a pixmap
507
specified. Returns \c true if the \a pixmap has been correctly inserted into
508
the cache; otherwise returns \c false.
509
510
The passed \a key is updated to reference \a pixmap now. Other copies of \a
511
key, if any, still refer to the old pixmap, which is, however, removed from
512
the cache by this function.
513
514
\sa setCacheLimit(), insert()
515
*/
516
#
endif
// QT_DEPRECATED_SINCE(6, 6)
517
518
/*!
519
Returns the cache limit (in kilobytes).
520
521
The default cache limit is 10240 KB.
522
523
\sa setCacheLimit()
524
*/
525
526
int
QPixmapCache
::cacheLimit()
527
{
528
if
(!
qt_pixmapcache_thread_test
(
)
)
529
return
0;
530
return
pm_cache()->maxCost();
531
}
532
533
/*!
534
Sets the cache limit to \a n kilobytes.
535
536
The default setting is 10240 KB.
537
538
\sa cacheLimit()
539
*/
540
541
void
QPixmapCache
::setCacheLimit(
int
n)
542
{
543
if
(!
qt_pixmapcache_thread_test
(
)
)
544
return
;
545
pm_cache()->setMaxCost(n);
546
}
547
548
/*!
549
Removes the pixmap associated with \a key from the cache.
550
*/
551
void
QPixmapCache
::remove(
const
QString &key)
552
{
553
if
(key.isEmpty() || !
qt_pixmapcache_thread_test
(
)
)
554
return
;
555
pm_cache()->remove(key);
556
}
557
558
/*!
559
Removes the pixmap associated with \a key from the cache and releases
560
the key for a future insertion.
561
*/
562
void
QPixmapCache
::remove(
const
Key &key)
563
{
564
if
(!
qt_pixmapcache_thread_test
(
)
)
565
return
;
566
//The key is not valid anymore, a flush happened before probably
567
if
(!key.d || !key.d->isValid)
568
return
;
569
pm_cache()->remove(key);
570
}
571
572
/*!
573
Removes all pixmaps from the cache.
574
*/
575
576
void
QPixmapCache
::clear()
577
{
578
if
(!QCoreApplication::closingDown() && !qt_pixmapcache_thread_test())
579
return
;
580
QT_TRY {
581
if
(pm_cache.exists())
582
pm_cache->clear();
583
} QT_CATCH(
const
std::bad_alloc &) {
584
// if we ran out of memory during pm_cache(), it's no leak,
585
// so just ignore it.
586
}
587
}
588
589
Q_AUTOTEST_EXPORT
void
qt_qpixmapcache_flush_detached_pixmaps
()
// for tst_qpixmapcache
590
{
591
if
(!
qt_pixmapcache_thread_test
(
)
)
592
return
;
593
pm_cache()->flushDetachedPixmaps(
true
);
594
}
595
596
Q_AUTOTEST_EXPORT
int
qt_qpixmapcache_qpixmapcache_total_used
()
// for tst_qpixmapcache
597
{
598
if
(!
qt_pixmapcache_thread_test
(
)
)
599
return
0;
600
return
(pm_cache()->totalCost()+1023) / 1024;
601
}
602
603
/*!
604
\fn QPixmapCache::KeyData::KeyData()
605
606
\internal
607
*/
608
/*!
609
\fn QPixmapCache::KeyData::KeyData(const KeyData &other)
610
\internal
611
*/
612
/*!
613
\fn QPixmapCache::KeyData::~KeyData()
614
615
\internal
616
*/
617
QT_END_NAMESPACE
QPMCache
Definition
qpixmapcache.cpp:179
QPMCache::remove
bool remove(const QString &key)
Definition
qpixmapcache.cpp:328
QPMCache::insert
bool insert(const QString &key, const QPixmap &pixmap, int cost)
Definition
qpixmapcache.cpp:299
QPMCache::releaseKey
void releaseKey(const QPixmapCache::Key &key)
Definition
qpixmapcache.cpp:362
QPMCache::clear
void clear()
Definition
qpixmapcache.cpp:378
QPMCache::getKeyData
static QPixmapCache::KeyData * getKeyData(QPixmapCache::Key *key)
Definition
qpixmapcache.cpp:395
QPMCache::~QPMCache
~QPMCache()
Definition
qpixmapcache.cpp:238
QPMCache::flushDetachedPixmaps
bool flushDetachedPixmaps(bool nt)
Definition
qpixmapcache.cpp:258
QPMCache::object
QPixmap * object(const QString &key) const
Definition
qpixmapcache.cpp:282
QPMCache::get
static QPixmapCache::KeyData * get(const QPixmapCache::Key &key)
Definition
qpixmapcache.cpp:199
QPMCache::resizeKeyArray
void resizeKeyArray(int size)
Definition
qpixmapcache.cpp:339
QPMCache::createKey
QPixmapCache::Key createKey()
Definition
qpixmapcache.cpp:350
QPMCache::insert
QPixmapCache::Key insert(const QPixmap &pixmap, int cost)
Definition
qpixmapcache.cpp:314
QPMCache::timerEvent
void timerEvent(QTimerEvent *) override
This event handler can be reimplemented in a subclass to receive timer events for the object.
Definition
qpixmapcache.cpp:270
QPixmapCacheEntry
Definition
qpixmapcache_p.h:43
QPixmapCacheEntry::~QPixmapCacheEntry
~QPixmapCacheEntry()
Definition
qpixmapcache.cpp:409
QPixmapCache::KeyData
Definition
qpixmapcache_p.h:28
cache_limit_default
static const int cache_limit_default
Definition
qpixmapcache.cpp:61
qt_pixmapcache_thread_test
static bool qt_pixmapcache_thread_test()
Definition
qpixmapcache.cpp:73
cost
static qsizetype cost(const QPixmap &pixmap)
Definition
qpixmapcache.cpp:63
qt_qpixmapcache_qpixmapcache_total_used
Q_AUTOTEST_EXPORT int qt_qpixmapcache_qpixmapcache_total_used()
Definition
qpixmapcache.cpp:596
qt_qpixmapcache_flush_detached_pixmaps
Q_AUTOTEST_EXPORT void qt_qpixmapcache_flush_detached_pixmaps()
Definition
qpixmapcache.cpp:589
qtbase
src
gui
image
qpixmapcache.cpp
Generated on
for Qt by
1.18.0