source: trunk/src/gui/image/qpixmapcache.cpp@ 651

Last change on this file since 651 was 651, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.2 sources.

File size: 17.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qpixmapcache.h"
43#include "qobject.h"
44#include "qdebug.h"
45#include "qpixmapcache_p.h"
46
47QT_BEGIN_NAMESPACE
48
49/*!
50 \class QPixmapCache
51
52 \brief The QPixmapCache class provides an application-wide cache for pixmaps.
53
54 \ingroup painting
55
56 This class is a tool for optimized drawing with QPixmap. You can
57 use it to store temporary pixmaps that are expensive to generate
58 without using more storage space than cacheLimit(). Use insert()
59 to insert pixmaps, find() to find them, and clear() to empty the
60 cache.
61
62 QPixmapCache contains no member data, only static functions to
63 access the global pixmap cache. It creates an internal QCache
64 object for caching the pixmaps.
65
66 The cache associates a pixmap with a user-provided string as a key,
67 or with a QPixmapCache::Key that the cache generates.
68 Using QPixmapCache::Key for keys is faster than using strings. The string API is
69 very convenient for complex keys but the QPixmapCache::Key API will be very
70 efficient and convenient for a one-to-one object-to-pixmap mapping \mdash in
71 this case, you can store the keys as members of an object.
72
73 If two pixmaps are inserted into the cache using equal keys then the
74 last pixmap will replace the first pixmap in the cache. This follows the
75 behavior of the QHash and QCache classes.
76
77 The cache becomes full when the total size of all pixmaps in the
78 cache exceeds cacheLimit(). The initial cache limit is
79 2048 KB (2 MB) on embedded platforms, 10240 KB (10 MB) on desktop
80 platforms; you can change this by calling setCacheLimit() with the
81 required value.
82 A pixmap takes roughly (\e{width} * \e{height} * \e{depth})/8 bytes of
83 memory.
84
85 The \e{Qt Quarterly} article
86 \l{http://qt.nokia.com/doc/qq/qq12-qpixmapcache.html}{Optimizing
87 with QPixmapCache} explains how to use QPixmapCache to speed up
88 applications by caching the results of painting.
89
90 \sa QCache, QPixmap
91*/
92
93#if defined(Q_OS_SYMBIAN)
94static int cache_limit = 1024; // 1048 KB cache limit for symbian
95#elif defined(Q_WS_QWS) || defined(Q_WS_WINCE)
96static int cache_limit = 2048; // 2048 KB cache limit for embedded
97#else
98static int cache_limit = 10240; // 10 MB cache limit for desktop
99#endif
100
101/*!
102 \class QPixmapCache::Key
103 \brief The QPixmapCache::Key class can be used for efficient access
104 to the QPixmapCache.
105 \since 4.6
106
107 Use QPixmapCache::insert() to receive an instance of Key generated
108 by the pixmap cache. You can store the key in your own objects for
109 a very efficient one-to-one object-to-pixmap mapping.
110*/
111
112/*!
113 Constructs an empty Key object.
114*/
115QPixmapCache::Key::Key() : d(0)
116{
117}
118
119/*!
120 \internal
121 Constructs a copy of \a other.
122*/
123QPixmapCache::Key::Key(const Key &other)
124{
125 if (other.d)
126 ++(other.d->ref);
127 d = other.d;
128}
129
130/*!
131 Destroys the key.
132*/
133QPixmapCache::Key::~Key()
134{
135 if (d && --(d->ref) == 0)
136 delete d;
137}
138
139/*!
140 \internal
141
142 Returns true if this key is the same as the given \a key; otherwise returns
143 false.
144*/
145bool QPixmapCache::Key::operator ==(const Key &key) const
146{
147 return (d == key.d);
148}
149
150/*!
151 \fn bool QPixmapCache::Key::operator !=(const Key &key) const
152 \internal
153*/
154
155/*!
156 \internal
157*/
158QPixmapCache::Key &QPixmapCache::Key::operator =(const Key &other)
159{
160 if (d != other.d) {
161 if (other.d)
162 ++(other.d->ref);
163 if (d && --(d->ref) == 0)
164 delete d;
165 d = other.d;
166 }
167 return *this;
168}
169
170class QPMCache : public QObject, public QCache<QPixmapCache::Key, QPixmapCacheEntry>
171{
172 Q_OBJECT
173public:
174 QPMCache();
175 ~QPMCache();
176
177 void timerEvent(QTimerEvent *);
178 bool insert(const QString& key, const QPixmap &pixmap, int cost);
179 QPixmapCache::Key insert(const QPixmap &pixmap, int cost);
180 bool replace(const QPixmapCache::Key &key, const QPixmap &pixmap, int cost);
181 bool remove(const QString &key);
182 bool remove(const QPixmapCache::Key &key);
183
184 void resizeKeyArray(int size);
185 QPixmapCache::Key createKey();
186 void releaseKey(const QPixmapCache::Key &key);
187 void clear();
188
189 QPixmap *object(const QString &key) const;
190 QPixmap *object(const QPixmapCache::Key &key) const;
191
192 static inline QPixmapCache::KeyData *get(const QPixmapCache::Key &key)
193 {return key.d;}
194
195 static QPixmapCache::KeyData* getKeyData(QPixmapCache::Key *key);
196
197private:
198 int *keyArray;
199 int theid;
200 int ps;
201 int keyArraySize;
202 int freeKey;
203 QHash<QString, QPixmapCache::Key> cacheKeys;
204 bool t;
205};
206
207QT_BEGIN_INCLUDE_NAMESPACE
208#include "qpixmapcache.moc"
209QT_END_INCLUDE_NAMESPACE
210
211uint qHash(const QPixmapCache::Key &k)
212{
213 return qHash(QPMCache::get(k)->key);
214}
215
216QPMCache::QPMCache()
217 : QObject(0),
218 QCache<QPixmapCache::Key, QPixmapCacheEntry>(cache_limit * 1024),
219 keyArray(0), theid(0), ps(0), keyArraySize(0), freeKey(0), t(false)
220{
221}
222QPMCache::~QPMCache()
223{
224 clear();
225 free(keyArray);
226}
227
228/*
229 This is supposed to cut the cache size down by about 25% in a
230 minute once the application becomes idle, to let any inserted pixmap
231 remain in the cache for some time before it becomes a candidate for
232 cleaning-up, and to not cut down the size of the cache while the
233 cache is in active use.
234
235 When the last pixmap has been deleted from the cache, kill the
236 timer so Qt won't keep the CPU from going into sleep mode.
237*/
238void QPMCache::timerEvent(QTimerEvent *)
239{
240 int mc = maxCost();
241 bool nt = totalCost() == ps;
242 setMaxCost(nt ? totalCost() * 3 / 4 : totalCost() -1);
243 setMaxCost(mc);
244 ps = totalCost();
245
246 QHash<QString, QPixmapCache::Key>::iterator it = cacheKeys.begin();
247 while (it != cacheKeys.end()) {
248 if (!contains(it.value())) {
249 releaseKey(it.value());
250 it = cacheKeys.erase(it);
251 } else {
252 ++it;
253 }
254 }
255
256 if (!size()) {
257 killTimer(theid);
258 theid = 0;
259 } else if (nt != t) {
260 killTimer(theid);
261 theid = startTimer(nt ? 10000 : 30000);
262 t = nt;
263 }
264}
265
266QPixmap *QPMCache::object(const QString &key) const
267{
268 QPixmapCache::Key cacheKey = cacheKeys.value(key);
269 if (!cacheKey.d || !cacheKey.d->isValid) {
270 const_cast<QPMCache *>(this)->cacheKeys.remove(key);
271 return 0;
272 }
273 QPixmap *ptr = QCache<QPixmapCache::Key, QPixmapCacheEntry>::object(cacheKey);
274 //We didn't find the pixmap in the cache, the key is not valid anymore
275 if (!ptr) {
276 const_cast<QPMCache *>(this)->cacheKeys.remove(key);
277 }
278 return ptr;
279}
280
281QPixmap *QPMCache::object(const QPixmapCache::Key &key) const
282{
283 Q_ASSERT(key.d->isValid);
284 QPixmap *ptr = QCache<QPixmapCache::Key, QPixmapCacheEntry>::object(key);
285 //We didn't find the pixmap in the cache, the key is not valid anymore
286 if (!ptr)
287 const_cast<QPMCache *>(this)->releaseKey(key);
288 return ptr;
289}
290
291bool QPMCache::insert(const QString& key, const QPixmap &pixmap, int cost)
292{
293 QPixmapCache::Key cacheKey;
294 QPixmapCache::Key oldCacheKey = cacheKeys.value(key);
295 //If for the same key we add already a pixmap we should delete it
296 if (oldCacheKey.d) {
297 QCache<QPixmapCache::Key, QPixmapCacheEntry>::remove(oldCacheKey);
298 cacheKeys.remove(key);
299 }
300
301 //we create a new key the old one has been removed
302 cacheKey = createKey();
303
304 bool success = QCache<QPixmapCache::Key, QPixmapCacheEntry>::insert(cacheKey, new QPixmapCacheEntry(cacheKey, pixmap), cost);
305 if (success) {
306 cacheKeys.insert(key, cacheKey);
307 if (!theid) {
308 theid = startTimer(30000);
309 t = false;
310 }
311 } else {
312 //Insertion failed we released the new allocated key
313 releaseKey(cacheKey);
314 }
315 return success;
316}
317
318QPixmapCache::Key QPMCache::insert(const QPixmap &pixmap, int cost)
319{
320 QPixmapCache::Key cacheKey = createKey();
321 bool success = QCache<QPixmapCache::Key, QPixmapCacheEntry>::insert(cacheKey, new QPixmapCacheEntry(cacheKey, pixmap), cost);
322 if (success) {
323 if (!theid) {
324 theid = startTimer(30000);
325 t = false;
326 }
327 } else {
328 //Insertion failed we released the key and return an invalid one
329 releaseKey(cacheKey);
330 }
331 return cacheKey;
332}
333
334bool QPMCache::replace(const QPixmapCache::Key &key, const QPixmap &pixmap, int cost)
335{
336 Q_ASSERT(key.d->isValid);
337 //If for the same key we had already an entry so we should delete the pixmap and use the new one
338 QCache<QPixmapCache::Key, QPixmapCacheEntry>::remove(key);
339
340 QPixmapCache::Key cacheKey = createKey();
341
342 bool success = QCache<QPixmapCache::Key, QPixmapCacheEntry>::insert(cacheKey, new QPixmapCacheEntry(cacheKey, pixmap), cost);
343 if (success) {
344 if(!theid) {
345 theid = startTimer(30000);
346 t = false;
347 }
348 const_cast<QPixmapCache::Key&>(key) = cacheKey;
349 } else {
350 //Insertion failed we released the key
351 releaseKey(cacheKey);
352 }
353 return success;
354}
355
356bool QPMCache::remove(const QString &key)
357{
358 QPixmapCache::Key cacheKey = cacheKeys.value(key);
359 //The key was not in the cache
360 if (!cacheKey.d)
361 return false;
362 cacheKeys.remove(key);
363 return QCache<QPixmapCache::Key, QPixmapCacheEntry>::remove(cacheKey);
364}
365
366bool QPMCache::remove(const QPixmapCache::Key &key)
367{
368 return QCache<QPixmapCache::Key, QPixmapCacheEntry>::remove(key);
369}
370
371void QPMCache::resizeKeyArray(int size)
372{
373 if (size <= keyArraySize || size == 0)
374 return;
375 keyArray = q_check_ptr(reinterpret_cast<int *>(realloc(keyArray,
376 size * sizeof(int))));
377 for (int i = keyArraySize; i != size; ++i)
378 keyArray[i] = i + 1;
379 keyArraySize = size;
380}
381
382QPixmapCache::Key QPMCache::createKey()
383{
384 if (freeKey == keyArraySize)
385 resizeKeyArray(keyArraySize ? keyArraySize << 1 : 2);
386 int id = freeKey;
387 freeKey = keyArray[id];
388 QPixmapCache::Key key;
389 QPixmapCache::KeyData *d = QPMCache::getKeyData(&key);
390 d->key = ++id;
391 return key;
392}
393
394void QPMCache::releaseKey(const QPixmapCache::Key &key)
395{
396 if (key.d->key > keyArraySize || key.d->key <= 0)
397 return;
398 key.d->key--;
399 keyArray[key.d->key] = freeKey;
400 freeKey = key.d->key;
401 key.d->isValid = false;
402 key.d->key = 0;
403}
404
405void QPMCache::clear()
406{
407 free(keyArray);
408 keyArray = 0;
409 freeKey = 0;
410 keyArraySize = 0;
411 //Mark all keys as invalid
412 QList<QPixmapCache::Key> keys = QCache<QPixmapCache::Key, QPixmapCacheEntry>::keys();
413 for (int i = 0; i < keys.size(); ++i)
414 keys.at(i).d->isValid = false;
415 QCache<QPixmapCache::Key, QPixmapCacheEntry>::clear();
416}
417
418QPixmapCache::KeyData* QPMCache::getKeyData(QPixmapCache::Key *key)
419{
420 if (!key->d)
421 key->d = new QPixmapCache::KeyData;
422 return key->d;
423}
424
425Q_GLOBAL_STATIC(QPMCache, pm_cache)
426
427int Q_AUTOTEST_EXPORT q_QPixmapCache_keyHashSize()
428{
429 return pm_cache()->size();
430}
431
432QPixmapCacheEntry::~QPixmapCacheEntry()
433{
434 pm_cache()->releaseKey(key);
435}
436
437/*!
438 \obsolete
439 \overload
440
441 Returns the pixmap associated with the \a key in the cache, or
442 null if there is no such pixmap.
443
444 \warning If valid, you should copy the pixmap immediately (this is
445 fast). Subsequent insertions into the cache could cause the
446 pointer to become invalid. For this reason, we recommend you use
447 bool find(const QString&, QPixmap*) instead.
448
449 Example:
450 \snippet doc/src/snippets/code/src_gui_image_qpixmapcache.cpp 0
451*/
452
453QPixmap *QPixmapCache::find(const QString &key)
454{
455 return pm_cache()->object(key);
456}
457
458
459/*!
460 \obsolete
461
462 Use bool find(const QString&, QPixmap*) instead.
463*/
464
465bool QPixmapCache::find(const QString &key, QPixmap& pixmap)
466{
467 return find(key, &pixmap);
468}
469
470/*!
471 Looks for a cached pixmap associated with the given \a key in the cache.
472 If the pixmap is found, the function sets \a pixmap to that pixmap and
473 returns true; otherwise it leaves \a pixmap alone and returns false.
474
475 \since 4.6
476
477 Example:
478 \snippet doc/src/snippets/code/src_gui_image_qpixmapcache.cpp 1
479*/
480
481bool QPixmapCache::find(const QString &key, QPixmap* pixmap)
482{
483 QPixmap *ptr = pm_cache()->object(key);
484 if (ptr && pixmap)
485 *pixmap = *ptr;
486 return ptr != 0;
487}
488
489/*!
490 Looks for a cached pixmap associated with the given \a key in the cache.
491 If the pixmap is found, the function sets \a pixmap to that pixmap and
492 returns true; otherwise it leaves \a pixmap alone and returns false. If
493 the pixmap is not found, it means that the \a key is no longer valid,
494 so it will be released for the next insertion.
495
496 \since 4.6
497*/
498bool QPixmapCache::find(const Key &key, QPixmap* pixmap)
499{
500 //The key is not valid anymore, a flush happened before probably
501 if (!key.d || !key.d->isValid)
502 return false;
503 QPixmap *ptr = pm_cache()->object(key);
504 if (ptr && pixmap)
505 *pixmap = *ptr;
506 return ptr != 0;
507}
508
509/*!
510 Inserts a copy of the pixmap \a pixmap associated with the \a key into
511 the cache.
512
513 All pixmaps inserted by the Qt library have a key starting with
514 "$qt", so your own pixmap keys should never begin "$qt".
515
516 When a pixmap is inserted and the cache is about to exceed its
517 limit, it removes pixmaps until there is enough room for the
518 pixmap to be inserted.
519
520 The oldest pixmaps (least recently accessed in the cache) are
521 deleted when more space is needed.
522
523 The function returns true if the object was inserted into the
524 cache; otherwise it returns false.
525
526 \sa setCacheLimit()
527*/
528
529bool QPixmapCache::insert(const QString &key, const QPixmap &pixmap)
530{
531 return pm_cache()->insert(key, pixmap, pixmap.width() * pixmap.height() * pixmap.depth() / 8);
532}
533
534/*!
535 Inserts a copy of the given \a pixmap into the cache and returns a key
536 that can be used to retrieve it.
537
538 When a pixmap is inserted and the cache is about to exceed its
539 limit, it removes pixmaps until there is enough room for the
540 pixmap to be inserted.
541
542 The oldest pixmaps (least recently accessed in the cache) are
543 deleted when more space is needed.
544
545 \sa setCacheLimit(), replace()
546
547 \since 4.6
548*/
549QPixmapCache::Key QPixmapCache::insert(const QPixmap &pixmap)
550{
551 return pm_cache()->insert(pixmap, pixmap.width() * pixmap.height() * pixmap.depth() / 8);
552}
553
554/*!
555 Replaces the pixmap associated with the given \a key with the \a pixmap
556 specified. Returns true if the \a pixmap has been correctly inserted into
557 the cache; otherwise returns false.
558
559 \sa setCacheLimit(), insert()
560
561 \since 4.6
562*/
563bool QPixmapCache::replace(const Key &key, const QPixmap &pixmap)
564{
565 //The key is not valid anymore, a flush happened before probably
566 if (!key.d || !key.d->isValid)
567 return false;
568 return pm_cache()->replace(key, pixmap, pixmap.width() * pixmap.height() * pixmap.depth() / 8);
569}
570
571/*!
572 Returns the cache limit (in kilobytes).
573
574 The default cache limit is 2048 KB on embedded platforms, 10240 KB on
575 desktop platforms.
576
577 \sa setCacheLimit()
578*/
579
580int QPixmapCache::cacheLimit()
581{
582 return cache_limit;
583}
584
585/*!
586 Sets the cache limit to \a n kilobytes.
587
588 The default setting is 2048 KB on embedded platforms, 10240 KB on
589 desktop platforms.
590
591 \sa cacheLimit()
592*/
593
594void QPixmapCache::setCacheLimit(int n)
595{
596 cache_limit = n;
597 pm_cache()->setMaxCost(1024 * cache_limit);
598}
599
600/*!
601 Removes the pixmap associated with \a key from the cache.
602*/
603void QPixmapCache::remove(const QString &key)
604{
605 pm_cache()->remove(key);
606}
607
608/*!
609 Removes the pixmap associated with \a key from the cache and releases
610 the key for a future insertion.
611
612 \since 4.6
613*/
614void QPixmapCache::remove(const Key &key)
615{
616 //The key is not valid anymore, a flush happened before probably
617 if (!key.d || !key.d->isValid)
618 return;
619 pm_cache()->remove(key);
620}
621
622/*!
623 Removes all pixmaps from the cache.
624*/
625
626void QPixmapCache::clear()
627{
628 QT_TRY {
629 pm_cache()->clear();
630 } QT_CATCH(const std::bad_alloc &) {
631 // if we ran out of memory during pm_cache(), it's no leak,
632 // so just ignore it.
633 }
634}
635
636QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.