QtBase  v6.3.1
qpixmapcache.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #define Q_TEST_QPIXMAPCACHE
41 #include "qpixmapcache.h"
42 #include "qobject.h"
43 #include "qdebug.h"
44 #include "qpixmapcache_p.h"
45 #include "qthread.h"
46 #include "qcoreapplication.h"
47 
49 
94 static const int cache_limit_default = 10240; // 10 MB cache limit
95 
96 static inline qsizetype cost(const QPixmap &pixmap)
97 {
98  // make sure to do a 64bit calculation; qsizetype might be smaller
99  const qint64 costKb = static_cast<qint64>(pixmap.width())
100  * pixmap.height() * pixmap.depth() / (8 * 1024);
101  const qint64 costMax = std::numeric_limits<qsizetype>::max();
102  // a small pixmap should have at least a cost of 1(kb)
103  return static_cast<qsizetype>(qBound(1LL, costKb, costMax));
104 }
105 
106 static inline bool qt_pixmapcache_thread_test()
107 {
109  return true;
110 
111  return false;
112 }
113 
130 {
131 }
132 
138 {
139  if (other.d)
140  ++(other.d->ref);
141  d = other.d;
142 }
143 
148 {
149  if (d && --(d->ref) == 0)
150  delete d;
151 }
152 
160 {
161  return (d == key.d);
162 }
163 
193 {
194  return d && d->isValid;
195 }
196 
201 {
202  if (d != other.d) {
203  if (other.d)
204  ++(other.d->ref);
205  if (d && --(d->ref) == 0)
206  delete d;
207  d = other.d;
208  }
209  return *this;
210 }
211 
212 class QPMCache : public QObject, public QCache<QPixmapCache::Key, QPixmapCacheEntry>
213 {
214  Q_OBJECT
215 public:
216  QPMCache();
217  ~QPMCache();
218 
219  void timerEvent(QTimerEvent *) override;
220  bool insert(const QString& key, const QPixmap &pixmap, int cost);
221  QPixmapCache::Key insert(const QPixmap &pixmap, int cost);
222  bool replace(const QPixmapCache::Key &key, const QPixmap &pixmap, int cost);
223  bool remove(const QString &key);
224  bool remove(const QPixmapCache::Key &key);
225 
226  void resizeKeyArray(int size);
227  QPixmapCache::Key createKey();
228  void releaseKey(const QPixmapCache::Key &key);
229  void clear();
230 
231  QPixmap *object(const QString &key) const;
232  QPixmap *object(const QPixmapCache::Key &key) const;
233 
235  {return key.d;}
236 
237  static QPixmapCache::KeyData* getKeyData(QPixmapCache::Key *key);
238 
239  bool flushDetachedPixmaps(bool nt);
240 
241 private:
242  enum { soon_time = 10000, flush_time = 30000 };
243  int *keyArray;
244  int theid;
245  int ps;
246  int keyArraySize;
247  int freeKey;
249  bool t;
250 };
251 
253 #include "qpixmapcache.moc"
255 
256 size_t qHash(const QPixmapCache::Key &k, size_t seed)
257 {
258  const auto *keyData = QPMCache::get(k);
259  return qHash(keyData ? keyData->key : 0, seed);
260 }
261 
263  : QObject(nullptr),
264  QCache<QPixmapCache::Key, QPixmapCacheEntry>(cache_limit_default),
265  keyArray(nullptr), theid(0), ps(0), keyArraySize(0), freeKey(0), t(false)
266 {
267 }
269 {
270  clear();
271  free(keyArray);
272 }
273 
274 /*
275  This is supposed to cut the cache size down by about 25% in a
276  minute once the application becomes idle, to let any inserted pixmap
277  remain in the cache for some time before it becomes a candidate for
278  cleaning-up, and to not cut down the size of the cache while the
279  cache is in active use.
280 
281  When the last detached pixmap has been deleted from the cache, kill the
282  timer so Qt won't keep the CPU from going into sleep mode. Currently
283  the timer is not restarted when the pixmap becomes unused, but it does
284  restart once something else is added (i.e. the cache space is actually needed).
285 
286  Returns \c true if any were removed.
287 */
289 {
290  auto mc = maxCost();
291  const qsizetype currentTotal = totalCost();
292  if (currentTotal)
293  setMaxCost(nt ? currentTotal * 3 / 4 : currentTotal - 1);
294  setMaxCost(mc);
295  ps = totalCost();
296 
297  bool any = false;
299  while (it != cacheKeys.end()) {
300  const auto value = it.value();
301  if (value.isValid() && !contains(value)) {
302  releaseKey(value);
303  it = cacheKeys.erase(it);
304  any = true;
305  } else {
306  ++it;
307  }
308  }
309 
310  return any;
311 }
312 
314 {
315  bool nt = totalCost() == ps;
316  if (!flushDetachedPixmaps(nt)) {
317  killTimer(theid);
318  theid = 0;
319  } else if (nt != t) {
320  killTimer(theid);
321  theid = startTimer(nt ? soon_time : flush_time);
322  t = nt;
323  }
324 }
325 
326 
328 {
329  QPixmapCache::Key cacheKey = cacheKeys.value(key);
330  if (!cacheKey.d || !cacheKey.d->isValid) {
331  const_cast<QPMCache *>(this)->cacheKeys.remove(key);
332  return nullptr;
333  }
335  //We didn't find the pixmap in the cache, the key is not valid anymore
336  if (!ptr) {
337  const_cast<QPMCache *>(this)->cacheKeys.remove(key);
338  }
339  return ptr;
340 }
341 
343 {
344  Q_ASSERT(key.isValid());
346  //We didn't find the pixmap in the cache, the key is not valid anymore
347  if (!ptr)
348  const_cast<QPMCache *>(this)->releaseKey(key);
349  return ptr;
350 }
351 
352 bool QPMCache::insert(const QString& key, const QPixmap &pixmap, int cost)
353 {
354  QPixmapCache::Key &cacheKey = cacheKeys[key];
355  //If for the same key we add already a pixmap we should delete it
356  if (cacheKey.d)
358 
359  //we create a new key the old one has been removed
360  cacheKey = createKey();
361 
362  bool success = QCache<QPixmapCache::Key, QPixmapCacheEntry>::insert(cacheKey, new QPixmapCacheEntry(cacheKey, pixmap), cost);
363  if (success) {
364  if (!theid) {
365  theid = startTimer(flush_time);
366  t = false;
367  }
368  } else {
369  //Insertion failed we released the new allocated key
370  cacheKeys.remove(key);
371  }
372  return success;
373 }
374 
376 {
377  QPixmapCache::Key cacheKey = createKey();
378  bool success = QCache<QPixmapCache::Key, QPixmapCacheEntry>::insert(cacheKey, new QPixmapCacheEntry(cacheKey, pixmap), cost);
379  if (success) {
380  if (!theid) {
381  theid = startTimer(flush_time);
382  t = false;
383  }
384  }
385  return cacheKey;
386 }
387 
388 bool QPMCache::replace(const QPixmapCache::Key &key, const QPixmap &pixmap, int cost)
389 {
390  Q_ASSERT(key.isValid());
391  //If for the same key we had already an entry so we should delete the pixmap and use the new one
393 
394  QPixmapCache::Key cacheKey = createKey();
395 
396  bool success = QCache<QPixmapCache::Key, QPixmapCacheEntry>::insert(cacheKey, new QPixmapCacheEntry(cacheKey, pixmap), cost);
397  if (success) {
398  if (!theid) {
399  theid = startTimer(flush_time);
400  t = false;
401  }
402  const_cast<QPixmapCache::Key&>(key) = cacheKey;
403  }
404  return success;
405 }
406 
408 {
409  auto cacheKey = cacheKeys.constFind(key);
410  //The key was not in the cache
411  if (cacheKey == cacheKeys.constEnd())
412  return false;
413  const bool result = QCache<QPixmapCache::Key, QPixmapCacheEntry>::remove(cacheKey.value());
414  cacheKeys.erase(cacheKey);
415  return result;
416 }
417 
419 {
421 }
422 
424 {
425  if (size <= keyArraySize || size == 0)
426  return;
427  keyArray = q_check_ptr(static_cast<int *>(realloc(keyArray,
428  size * sizeof(int))));
429  for (int i = keyArraySize; i != size; ++i)
430  keyArray[i] = i + 1;
431  keyArraySize = size;
432 }
433 
435 {
436  if (freeKey == keyArraySize)
437  resizeKeyArray(keyArraySize ? keyArraySize << 1 : 2);
438  int id = freeKey;
439  freeKey = keyArray[id];
442  d->key = ++id;
443  return key;
444 }
445 
447 {
448  QPixmapCache::KeyData *keyData = key.d;
449  if (!keyData || keyData->key > keyArraySize || keyData->key <= 0)
450  return;
451  keyData->key--;
452  keyArray[keyData->key] = freeKey;
453  freeKey = keyData->key;
454  keyData->isValid = false;
455  keyData->key = 0;
456 }
457 
459 {
460  free(keyArray);
461  keyArray = nullptr;
462  freeKey = 0;
463  keyArraySize = 0;
464  //Mark all keys as invalid
466  for (const auto &key : keys) {
467  if (key.d)
468  key.d->isValid = false;
469  }
471  // Nothing left to flush; stop the timer
472  if (theid) {
473  killTimer(theid);
474  theid = 0;
475  }
476 }
477 
479 {
480  if (!key->d)
481  key->d = new QPixmapCache::KeyData;
482  return key->d;
483 }
484 
485 Q_GLOBAL_STATIC(QPMCache, pm_cache)
486 
488 {
489  return pm_cache()->size();
490 }
491 
493 {
494  pm_cache()->releaseKey(key);
495 }
496 
509 {
510  if (!qt_pixmapcache_thread_test())
511  return false;
512  QPixmap *ptr = pm_cache()->object(key);
513  if (ptr && pixmap)
514  *pixmap = *ptr;
515  return ptr != nullptr;
516 }
517 
528 {
529  if (!qt_pixmapcache_thread_test())
530  return false;
531  //The key is not valid anymore, a flush happened before probably
532  if (!key.d || !key.d->isValid)
533  return false;
534  QPixmap *ptr = pm_cache()->object(key);
535  if (ptr && pixmap)
536  *pixmap = *ptr;
537  return ptr != nullptr;
538 }
539 
561 {
562  if (!qt_pixmapcache_thread_test())
563  return false;
564  return pm_cache()->insert(key, pixmap, cost(pixmap));
565 }
566 
583 {
584  if (!qt_pixmapcache_thread_test())
585  return QPixmapCache::Key();
586  return pm_cache()->insert(pixmap, cost(pixmap));
587 }
588 
599 {
600  if (!qt_pixmapcache_thread_test())
601  return false;
602  //The key is not valid anymore, a flush happened before probably
603  if (!key.d || !key.d->isValid)
604  return false;
605  return pm_cache()->replace(key, pixmap, cost(pixmap));
606 }
607 
617 {
618  if (!qt_pixmapcache_thread_test())
619  return 0;
620  return pm_cache()->maxCost();
621 }
622 
632 {
633  if (!qt_pixmapcache_thread_test())
634  return;
635  pm_cache()->setMaxCost(n);
636 }
637 
642 {
643  if (!qt_pixmapcache_thread_test())
644  return;
645  pm_cache()->remove(key);
646 }
647 
655 {
656  if (!qt_pixmapcache_thread_test())
657  return;
658  //The key is not valid anymore, a flush happened before probably
659  if (!key.d || !key.d->isValid)
660  return;
661  pm_cache()->remove(key);
662 }
663 
669 {
670  if (!QCoreApplication::closingDown() && !qt_pixmapcache_thread_test())
671  return;
672  QT_TRY {
673  if (pm_cache.exists())
674  pm_cache->clear();
675  } QT_CATCH(const std::bad_alloc &) {
676  // if we ran out of memory during pm_cache(), it's no leak,
677  // so just ignore it.
678  }
679 }
680 
681 void QPixmapCache::flushDetachedPixmaps()
682 {
683  if (!qt_pixmapcache_thread_test())
684  return;
685  pm_cache()->flushDetachedPixmaps(true);
686 }
687 
688 int QPixmapCache::totalUsed()
689 {
690  if (!qt_pixmapcache_thread_test())
691  return 0;
692  return (pm_cache()->totalCost()+1023) / 1024;
693 }
694 
small capitals from c petite p scientific i
[1]
Definition: afcover.h:80
Definition: qcache.h:50
bool remove(const Key &key) noexcept(std::is_nothrow_destructible_v< Node >)
Definition: qcache.h:256
void setMaxCost(qsizetype m) noexcept(std::is_nothrow_destructible_v< Node >)
Definition: qcache.h:188
bool contains(const QPixmapCache::Key &key) const noexcept
Definition: qcache.h:251
qsizetype maxCost() const noexcept
Definition: qcache.h:187
qsizetype totalCost() const noexcept
Definition: qcache.h:193
T * object(const Key &key) const noexcept
Definition: qcache.h:243
qsizetype size() const noexcept
Definition: qcache.h:195
void clear() noexcept(std::is_nothrow_destructible_v< Node >)
Definition: qcache.h:210
bool insert(const Key &key, T *object, qsizetype cost=1)
Definition: qcache.h:218
QList< QPixmapCache::Key > keys() const
Definition: qcache.h:198
static QCoreApplication * instance()
static bool closingDown()
bool remove(const Key &key)
Definition: qhash.h:911
iterator begin()
Definition: qhash.h:1155
const_iterator constFind(const Key &key) const noexcept
Definition: qhash.h:1224
const_iterator constEnd() const noexcept
Definition: qhash.h:1162
iterator erase(const_iterator it)
Definition: qhash.h:1172
T value(const Key &key) const noexcept
Definition: qhash.h:997
iterator end() noexcept
Definition: qhash.h:1159
The QObject class is the base class of all Qt objects.
Definition: qobject.h:125
int startTimer(int interval, Qt::TimerType timerType=Qt::CoarseTimer)
Definition: qobject.cpp:1765
void killTimer(int id)
Definition: qobject.cpp:1838
bool remove(const QString &key)
bool insert(const QString &key, const QPixmap &pixmap, int cost)
void releaseKey(const QPixmapCache::Key &key)
void clear()
static QPixmapCache::KeyData * getKeyData(QPixmapCache::Key *key)
bool flushDetachedPixmaps(bool nt)
static QPixmapCache::KeyData * get(const QPixmapCache::Key &key)
QPixmap * object(const QString &key) const
void resizeKeyArray(int size)
QPixmapCache::Key createKey()
bool replace(const QPixmapCache::Key &key, const QPixmap &pixmap, int cost)
void timerEvent(QTimerEvent *) override
The QPixmapCache::Key class can be used for efficient access to the QPixmapCache. \inmodule QtGui.
Definition: qpixmapcache.h:54
Key & operator=(Key &&other) noexcept
Definition: qpixmapcache.h:59
bool isValid() const noexcept
bool operator==(const Key &key) const
~QPixmapCacheEntry()
The QPixmapCache class provides an application-wide cache for pixmaps.
Definition: qpixmapcache.h:50
static bool find(const QString &key, QPixmap *pixmap)
static void remove(const QString &key)
static int cacheLimit()
static bool insert(const QString &key, const QPixmap &pixmap)
static void setCacheLimit(int)
static void clear()
static bool replace(const Key &key, const QPixmap &pixmap)
The QPixmap class is an off-screen image representation that can be used as a paint device.
Definition: qpixmap.h:63
The QString class provides a Unicode character string.
Definition: qstring.h:388
static QThread * currentThread()
Definition: qthread.cpp:879
The QTimerEvent class contains parameters that describe a timer event.
Definition: qcoreevent.h:367
QRhiGraphicsPipeline * ps
auto it unsigned count const
Definition: hb-iter.hh:848
#define Q_LIKELY(x)
EGLOutputLayerEXT EGLint EGLAttrib value
#define QT_BEGIN_INCLUDE_NAMESPACE
Definition: qglobal.h:265
#define Q_AUTOTEST_EXPORT
Definition: qglobal.h:579
#define QT_END_INCLUDE_NAMESPACE
Definition: qglobal.h:266
ptrdiff_t qsizetype
Definition: qglobal.h:308
long long qint64
Definition: qglobal.h:298
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
GLenum GLuint id
[6]
Definition: qopengl.h:270
GLuint64 key
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint object
[3]
GLfloat n
GLdouble GLdouble t
[9]
Definition: qopenglext.h:243
GLuint64EXT * result
[6]
Definition: qopenglext.h:10932
int Q_AUTOTEST_EXPORT q_QPixmapCache_keyHashSize()
QT_BEGIN_INCLUDE_NAMESPACE QT_END_INCLUDE_NAMESPACE size_t qHash(const QPixmapCache::Key &k, size_t seed)
#define Q_ASSERT(cond)
Definition: qrandom.cpp:84
#define Q_OBJECT
Definition: qtmetamacros.h:158
QObject::connect nullptr
QSharedPointer< T > other(t)
[5]
widget render & pixmap
QStringList::Iterator it