• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.14.38 API Reference
  • KDE Home
  • Contact Us
 

KDEUI

  • kdeui
  • util
kpixmapcache.cpp
Go to the documentation of this file.
1/*
2 *
3 * This file is part of the KDE project.
4 * Copyright (C) 2007 Rivo Laks <rivolaks@hot.ee>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License version 2 as published by the Free Software Foundation.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21#include "kpixmapcache.h"
22
23#include <QtCore/QString>
24#include <QtGui/QPixmap>
25#include <QtCore/QFile>
26#include <QtCore/QDataStream>
27#include <QtCore/QFileInfo>
28#include <QtCore/QDateTime>
29#include <QtGui/QPixmapCache>
30#include <QtCore/QtGlobal>
31#include <QtGui/QPainter>
32#include <QtCore/QQueue>
33#include <QtCore/QTimer>
34#include <QtCore/QMutex>
35#include <QtCore/QMutexLocker>
36#include <QtCore/QList>
37
38#include <kglobal.h>
39#include <kstandarddirs.h>
40#include <kdebug.h>
41#include <klockfile.h>
42#include <ksavefile.h>
43#ifndef _WIN32_WCE
44#include <ksvgrenderer.h>
45#endif
46#include <kdefakes.h>
47
48#include <config.h>
49
50#include <time.h>
51#include <unistd.h>
52#include <sys/types.h>
53#include <string.h>
54
55#if defined(HAVE_MADVISE)
56#include <sys/mman.h>
57#endif
58
59//#define DISABLE_PIXMAPCACHE
60
61#ifdef Q_OS_SOLARIS
62#ifndef _XPG_4_2
63extern "C" int madvise(caddr_t addr, size_t len, int advice);
64#endif
65#endif
66
67#define KPIXMAPCACHE_VERSION 0x000208
68
69namespace {
70
71class KPCLockFile
72{
73public:
74 KPCLockFile(const QString& filename)
75 {
76 mValid = false;
77 mLockFile = new KLockFile(filename);
78 // Try to lock the file up to 5 times, waiting 5 ms between retries
79 KLockFile::LockResult result;
80 for (int i = 0; i < 5; i++) {
81 result = mLockFile->lock(KLockFile::NoBlockFlag);
82 if (result == KLockFile::LockOK) {
83 mValid = true;
84 break;
85 }
86 usleep(5*1000);
87 }
88 // Output error msg if locking failed
89 if (!mValid) {
90 kError() << "Failed to lock file" << filename << ", last result =" << result;
91 }
92 }
93 ~KPCLockFile()
94 {
95 unlock();
96 delete mLockFile;
97 }
98
99 void unlock()
100 {
101 if (mValid) {
102 mLockFile->unlock();
103 mValid = false;
104 }
105 }
106
107 bool isValid() const { return mValid; }
108
109private:
110 bool mValid;
111 KLockFile* mLockFile;
112};
113
114// Contained in the header so we will know if we created this or not. Older
115// versions of kdelibs had the version on the byte right after "CACHE ".
116// "DEUX" will be read as a quint32 by such versions, and will always be
117// greater than the last such version (0x000207), whether a big-endian or
118// little-endian system is used. Therefore older systems will correctly
119// recognize that this is from a newer kdelibs. (This is an issue since old
120// and new kdelibs do not read the version from the exact same spot.)
121static const char KPC_MAGIC[] = "KDE PIXMAP CACHE DEUX";
122struct KPixmapCacheDataHeader
123{
124 // -1 from sizeof so we don't write out the trailing null. If you change
125 // the list of members change them in the KPixmapCacheIndexHeader as well!
126 char magic[sizeof(KPC_MAGIC) - 1];
127 quint32 cacheVersion;
128 quint32 size;
129};
130
131struct KPixmapCacheIndexHeader
132{
133 // -1 from sizeof so we don't write out the trailing null.
134 // The follow are also in KPixmapCacheDataHeader
135 char magic[sizeof(KPC_MAGIC) - 1];
136 quint32 cacheVersion;
137 quint32 size;
138
139 // These belong only to this header type.
140 quint32 cacheId;
141 time_t timestamp;
142};
143
144class KPCMemoryDevice : public QIODevice
145{
146public:
147 KPCMemoryDevice(char* start, quint32* size, quint32 available);
148 virtual ~KPCMemoryDevice();
149
150 virtual qint64 size() const { return *mSize; }
151 void setSize(quint32 s) { *mSize = s; }
152 virtual bool seek(qint64 pos);
153
154protected:
155 virtual qint64 readData(char* data, qint64 maxSize);
156 virtual qint64 writeData(const char* data, qint64 maxSize);
157
158private:
159 char* mMemory;
160 KPixmapCacheIndexHeader *mHeader; // alias of mMemory
161 quint32* mSize;
162 quint32 mInitialSize;
163 qint64 mAvailable;
164 quint32 mPos;
165};
166
167KPCMemoryDevice::KPCMemoryDevice(char* start, quint32* size, quint32 available) : QIODevice()
168{
169 mMemory = start;
170 mHeader = reinterpret_cast<KPixmapCacheIndexHeader *>(start);
171 mSize = size;
172 mAvailable = available;
173 mPos = 0;
174
175 this->open(QIODevice::ReadWrite);
176
177 // Load up-to-date size from the memory
178 *mSize = mHeader->size;
179
180 mInitialSize = *mSize;
181}
182
183KPCMemoryDevice::~KPCMemoryDevice()
184{
185 if (*mSize != mInitialSize) {
186 // Update file size
187 mHeader->size = *mSize;
188 }
189}
190
191bool KPCMemoryDevice::seek(qint64 pos)
192{
193 if (pos < 0 || pos > *mSize) {
194 return false;
195 }
196 mPos = pos;
197 return QIODevice::seek(pos);
198}
199
200qint64 KPCMemoryDevice::readData(char* data, qint64 len)
201{
202 len = qMin(len, qint64(*mSize) - mPos);
203 if (len <= 0) {
204 return 0;
205 }
206 memcpy(data, mMemory + mPos, len);
207 mPos += len;
208 return len;
209}
210
211qint64 KPCMemoryDevice::writeData(const char* data, qint64 len)
212{
213 if (mPos + len > mAvailable) {
214 kError() << "Overflow of" << mPos+len - mAvailable;
215 return -1;
216 }
217 memcpy(mMemory + mPos, (uchar*)data, len);
218 mPos += len;
219 *mSize = qMax(*mSize, mPos);
220 return len;
221}
222
223
224} // namespace
225
226class KPixmapCache::Private
227{
228public:
229 Private(KPixmapCache* q);
230 ~Private();
231
232 // Return device used to read from index or data file. The device is either
233 // QFile or KPCMemoryDevice (if mmap is used)
234 QIODevice* indexDevice();
235 QIODevice* dataDevice();
236
237 // Unmmaps any currently mmapped files and then tries to (re)mmap the cache
238 // files. If mmapping is disabled then it does nothing
239 bool mmapFiles();
240 void unmmapFiles();
241 // Marks the shared mmapped files as invalid so that all processes will
242 // reload the files
243 void invalidateMmapFiles();
244
245 // List of all KPixmapCache::Private instances in this process.
246 static QList<KPixmapCache::Private *> mCaches;
247
248 static unsigned kpcNumber; // Used to setup for qpcKey
249
250 int findOffset(const QString& key);
251 int binarySearchKey(QDataStream& stream, const QString& key, int start);
252 void writeIndexEntry(QDataStream& stream, const QString& key, int dataoffset);
253
254 bool checkLockFile();
255 bool checkFileVersion(const QString& filename);
256 bool loadIndexHeader();
257 bool loadDataHeader();
258
259 bool removeEntries(int newsize);
260 bool scheduleRemoveEntries(int newsize);
261
262 void init();
263 bool loadData(int offset, QPixmap& pix);
264 int writeData(const QString& key, const QPixmap& pix);
265 void writeIndex(const QString& key, int offset);
266
267 // Prepends key's hash to the key. This makes comparisons and key
268 // lookups faster as the beginnings of the keys are more random
269 QString indexKey(const QString& key);
270
271 // Returns a QString suitable for use in the static QPixmapCache, which
272 // differentiates each KPC object in the process.
273 QString qpcKey(const QString& key) const;
274
275 KPixmapCache* q;
276
277 QString mThisString; // Used by qpcKey
278 quint32 mHeaderSize; // full size of the index header, including custom (subclass') header data
279 quint32 mIndexRootOffset; // offset of the first entry in index file
280
281 QString mName;
282 QString mIndexFile;
283 QString mDataFile;
284 QString mLockFileName;
285 QMutex mMutex;
286
287 quint32 mTimestamp;
288 quint32 mCacheId; // Unique id, will change when cache is recreated
289 int mCacheLimit;
290 RemoveStrategy mRemoveStrategy:4;
291 bool mUseQPixmapCache:4;
292
293 bool mInited:8; // Whether init() has been called (it's called on-demand)
294 bool mEnabled:8; // whether it's possible to use the cache
295 bool mValid:8; // whether cache has been inited and is ready to be used
296
297 // Holds info about mmapped file
298 struct MmapInfo
299 {
300 MmapInfo() { file = 0; indexHeader = 0; }
301 QFile* file; // If this is not null, then the file is mmapped
302
303 // This points to the mmap'ed file area.
304 KPixmapCacheIndexHeader *indexHeader;
305
306 quint32 size; // Number of currently used bytes
307 quint32 available; // Number of available bytes (including those reserved for mmap)
308 };
309 MmapInfo mIndexMmapInfo;
310 MmapInfo mDataMmapInfo;
311 // Mmaps given file, growing it to newsize bytes.
312 bool mmapFile(const QString& filename, MmapInfo* info, int newsize);
313 void unmmapFile(MmapInfo* info);
314
315
316 // Used by removeEntries()
317 class KPixmapCacheEntry
318 {
319 public:
320 KPixmapCacheEntry(int indexoffset_, const QString& key_, int dataoffset_,
321 int pos_, quint32 timesused_, quint32 lastused_)
322 : indexoffset(indexoffset_),
323 key(key_),
324 dataoffset(dataoffset_),
325 pos(pos_),
326 timesused(timesused_),
327 lastused(lastused_)
328 {
329 }
330
331 int indexoffset;
332 QString key;
333 int dataoffset;
334
335 int pos;
336 quint32 timesused;
337 quint32 lastused;
338 };
339
340 // Various comparison functions for different removal strategies
341 static bool compareEntriesByAge(const KPixmapCacheEntry& a, const KPixmapCacheEntry& b)
342 {
343 return a.pos > b.pos;
344 }
345 static bool compareEntriesByTimesUsed(const KPixmapCacheEntry& a, const KPixmapCacheEntry& b)
346 {
347 return a.timesused > b.timesused;
348 }
349 static bool compareEntriesByLastUsed(const KPixmapCacheEntry& a, const KPixmapCacheEntry& b)
350 {
351 return a.lastused > b.lastused;
352 }
353};
354
355// List of KPixmapCache::Private instances.
356QList<KPixmapCache::Private *> KPixmapCache::Private::mCaches;
357
358unsigned KPixmapCache::Private::kpcNumber = 0;
359
360KPixmapCache::Private::Private(KPixmapCache* _q)
361{
362 q = _q;
363 mCaches.append(this);
364 mThisString = QString("%1").arg(kpcNumber++);
365}
366
367KPixmapCache::Private::~Private()
368{
369 mCaches.removeAll(this);
370}
371
372bool KPixmapCache::Private::mmapFiles()
373{
374 unmmapFiles(); // Noop if nothing has been mmapped
375 if (!q->isValid()) {
376 return false;
377 }
378
379 //TODO: 100MB limit if we have no cache limit, is that sensible?
380 int cacheLimit = mCacheLimit > 0 ? mCacheLimit : 100 * 1024;
381 if (!mmapFile(mIndexFile, &mIndexMmapInfo, (int)(cacheLimit * 0.4 + 100) * 1024)) {
382 q->setValid(false);
383 return false;
384 }
385
386 if (!mmapFile(mDataFile, &mDataMmapInfo, (int)(cacheLimit * 1.5 + 500) * 1024)) {
387 unmmapFile(&mIndexMmapInfo);
388 q->setValid(false);
389 return false;
390 }
391
392 return true;
393}
394
395void KPixmapCache::Private::unmmapFiles()
396{
397 unmmapFile(&mIndexMmapInfo);
398 unmmapFile(&mDataMmapInfo);
399}
400
401void KPixmapCache::Private::invalidateMmapFiles()
402{
403 if (!q->isValid())
404 return;
405 // Set cache id to 0, this will force a reload the next time the files are used
406 if (mIndexMmapInfo.file) {
407 kDebug(264) << "Invalidating cache";
408 mIndexMmapInfo.indexHeader->cacheId = 0;
409 }
410}
411
412bool KPixmapCache::Private::mmapFile(const QString& filename, MmapInfo* info, int newsize)
413{
414 info->file = new QFile(filename);
415 if (!info->file->open(QIODevice::ReadWrite)) {
416 kDebug(264) << "Couldn't open" << filename;
417 delete info->file;
418 info->file = 0;
419 return false;
420 }
421
422 if (!info->size) {
423 info->size = info->file->size();
424 }
425 info->available = newsize;
426
427 // Only resize if greater than current file size, otherwise we may cause SIGBUS
428 // errors from mmap().
429 if (info->file->size() < info->available && !info->file->resize(info->available)) {
430 kError(264) << "Couldn't resize" << filename << "to" << newsize;
431 delete info->file;
432 info->file = 0;
433 return false;
434 }
435
436 //void* indexMem = mmap(0, info->available, PROT_READ | PROT_WRITE, MAP_SHARED, info->file->handle(), 0);
437 void *indexMem = info->file->map(0, info->available);
438 if (indexMem == 0) {
439 kError() << "mmap failed for" << filename;
440 delete info->file;
441 info->file = 0;
442 return false;
443 }
444 info->indexHeader = reinterpret_cast<KPixmapCacheIndexHeader *>(indexMem);
445#ifdef HAVE_MADVISE
446 posix_madvise(indexMem, info->size, POSIX_MADV_WILLNEED);
447#endif
448
449 info->file->close();
450
451 // Update our stored file size. Other objects that have this mmaped will have to
452 // invalidate their map if size is different.
453 if(0 == info->indexHeader->size) {
454 // This size includes index header and and custom headers tacked on
455 // by subclasses.
456 info->indexHeader->size = mHeaderSize;
457 info->size = info->indexHeader->size;
458 }
459
460 return true;
461}
462
463void KPixmapCache::Private::unmmapFile(MmapInfo* info)
464{
465 if (info->file) {
466 info->file->unmap(reinterpret_cast<uchar*>(info->indexHeader));
467 info->indexHeader = 0;
468 info->available = 0;
469 info->size = 0;
470
471 delete info->file;
472 info->file = 0;
473 }
474}
475
476
477QIODevice* KPixmapCache::Private::indexDevice()
478{
479 QIODevice* device = 0;
480
481 if (mIndexMmapInfo.file) {
482 // Make sure the file still exists
483 QFileInfo fi(mIndexFile);
484
485 if (!fi.exists() || fi.size() != mIndexMmapInfo.available) {
486 kDebug(264) << "File size has changed, re-initializing.";
487 q->recreateCacheFiles(); // Recreates memory maps as well.
488 }
489
490 fi.refresh();
491 if(fi.exists() && fi.size() == mIndexMmapInfo.available) {
492 // Create the device
493 device = new KPCMemoryDevice(
494 reinterpret_cast<char*>(mIndexMmapInfo.indexHeader),
495 &mIndexMmapInfo.size, mIndexMmapInfo.available);
496 }
497
498 // Is it possible to have a valid cache with no file? If not it would be easier
499 // to do return 0 in the else portion of the prior test.
500 if(!q->isValid()) {
501 delete device;
502 return 0;
503 }
504 }
505
506 if (!device) {
507 QFile* file = new QFile(mIndexFile);
508 if (!file->exists() || (size_t) file->size() < sizeof(KPixmapCacheIndexHeader)) {
509 q->recreateCacheFiles();
510 }
511
512 if (!q->isValid() || !file->open(QIODevice::ReadWrite)) {
513 kDebug(264) << "Couldn't open index file" << mIndexFile;
514 delete file;
515 return 0;
516 }
517
518 device = file;
519 }
520
521 // Make sure the device is up-to-date
522 KPixmapCacheIndexHeader indexHeader;
523
524 int numRead = device->read(reinterpret_cast<char *>(&indexHeader), sizeof indexHeader);
525 if (sizeof indexHeader != numRead) {
526 kError(264) << "Unable to read header from pixmap cache index.";
527 delete device;
528 return 0;
529 }
530
531 if (indexHeader.cacheId != mCacheId) {
532 kDebug(264) << "Cache has changed, reloading";
533 delete device;
534
535 init();
536 if (!q->isValid()) {
537 return 0;
538 } else {
539 return indexDevice(); // Careful, this is a recursive call.
540 }
541 }
542
543 return device;
544}
545
546QIODevice* KPixmapCache::Private::dataDevice()
547{
548 if (mDataMmapInfo.file) {
549 // Make sure the file still exists
550 QFileInfo fi(mDataFile);
551
552 if (!fi.exists() || fi.size() != mDataMmapInfo.available) {
553 kDebug(264) << "File size has changed, re-initializing.";
554 q->recreateCacheFiles(); // Recreates memory maps as well.
555
556 // Index file has also been recreated so we cannot continue with
557 // modifying the data file because it would make things inconsistent.
558 return 0;
559 }
560
561 fi.refresh();
562 if (fi.exists() && fi.size() == mDataMmapInfo.available) {
563 // Create the device
564 return new KPCMemoryDevice(
565 reinterpret_cast<char*>(mDataMmapInfo.indexHeader),
566 &mDataMmapInfo.size, mDataMmapInfo.available);
567 }
568 else
569 return 0;
570 }
571
572 QFile* file = new QFile(mDataFile);
573 if (!file->exists() || (size_t) file->size() < sizeof(KPixmapCacheDataHeader)) {
574 q->recreateCacheFiles();
575 // Index file has also been recreated so we cannot continue with
576 // modifying the data file because it would make things inconsistent.
577 delete file;
578 return 0;
579 }
580 if (!file->open(QIODevice::ReadWrite)) {
581 kDebug(264) << "Couldn't open data file" << mDataFile;
582 delete file;
583 return 0;
584 }
585 return file;
586}
587
588int KPixmapCache::Private::binarySearchKey(QDataStream& stream, const QString& key, int start)
589{
590 stream.device()->seek(start);
591
592 QString fkey;
593 qint32 foffset;
594 quint32 timesused, lastused;
595 qint32 leftchild, rightchild;
596 stream >> fkey >> foffset >> timesused >> lastused >> leftchild >> rightchild;
597
598 if (fkey.isEmpty()) {
599 return start;
600 }
601
602 if (key < fkey) {
603 if (leftchild) {
604 return binarySearchKey(stream, key, leftchild);
605 }
606 } else if (key == fkey) {
607 return start;
608 } else if (rightchild) {
609 return binarySearchKey(stream, key, rightchild);
610 }
611
612 return start;
613}
614
615int KPixmapCache::Private::findOffset(const QString& key)
616{
617 // Open device and datastream on it
618 QIODevice* device = indexDevice();
619 if (!device) {
620 return -1;
621 }
622 device->seek(mIndexRootOffset);
623 QDataStream stream(device);
624
625 // If we're already at the end of the stream then the root node doesn't
626 // exist yet and there are no entries. Otherwise, do a binary search
627 // starting from the root node.
628 if (!stream.atEnd()) {
629 // One exception is that the root node may exist but be invalid,
630 // which can happen when the cache data is discarded. This is
631 // represented by an empty fkey
632 QString fkey;
633 stream >> fkey;
634
635 if (fkey.isEmpty()) {
636 delete device;
637 return -1;
638 }
639
640 int nodeoffset = binarySearchKey(stream, key, mIndexRootOffset);
641
642 // Load the found entry and check if it's the one we're looking for.
643 device->seek(nodeoffset);
644 stream >> fkey;
645
646 if (fkey == key) {
647 // Read offset and statistics
648 qint32 foffset;
649 quint32 timesused, lastused;
650 stream >> foffset >> timesused;
651 // Update statistics
652 timesused++;
653 lastused = ::time(0);
654 stream.device()->seek(stream.device()->pos() - sizeof(quint32));
655 stream << timesused << lastused;
656 delete device;
657 return foffset;
658 }
659 }
660
661 // Nothing was found
662 delete device;
663 return -1;
664}
665
666bool KPixmapCache::Private::checkLockFile()
667{
668 // For KLockFile we need to ensure the lock file doesn't exist.
669 if (QFile::exists(mLockFileName)) {
670 if (!QFile::remove(mLockFileName)) {
671 kError() << "Couldn't remove lockfile" << mLockFileName;
672 return false;
673 }
674 }
675 return true;
676}
677
678bool KPixmapCache::Private::checkFileVersion(const QString& filename)
679{
680 if (!mEnabled) {
681 return false;
682 }
683
684 if (QFile::exists(filename)) {
685 // File already exists, make sure it can be opened
686 QFile f(filename);
687 if (!f.open(QIODevice::ReadOnly)) {
688 kError() << "Couldn't open file" << filename;
689 return false;
690 }
691
692 // The index header is the same as the beginning of the data header (on purpose),
693 // so use index header for either one.
694 KPixmapCacheIndexHeader indexHeader;
695
696 // Ensure we have a valid cache.
697 if(sizeof indexHeader != f.read(reinterpret_cast<char*>(&indexHeader), sizeof indexHeader) ||
698 qstrncmp(indexHeader.magic, KPC_MAGIC, sizeof(indexHeader.magic)) != 0)
699 {
700 kDebug(264) << "File" << filename << "is not KPixmapCache file, or is";
701 kDebug(264) << "version <= 0x000207, will recreate...";
702 return q->recreateCacheFiles();
703 }
704
705 if(indexHeader.cacheVersion == KPIXMAPCACHE_VERSION)
706 return true;
707
708 // Don't recreate the cache if it has newer version to avoid
709 // problems when upgrading kdelibs.
710 if(indexHeader.cacheVersion > KPIXMAPCACHE_VERSION) {
711 kDebug(264) << "File" << filename << "has newer version, disabling cache";
712 return false;
713 }
714
715 kDebug(264) << "File" << filename << "is outdated, will recreate...";
716 }
717
718 return q->recreateCacheFiles();
719}
720
721bool KPixmapCache::Private::loadDataHeader()
722{
723 // Open file and datastream on it
724 QFile file(mDataFile);
725 if (!file.open(QIODevice::ReadOnly)) {
726 return false;
727 }
728
729 KPixmapCacheDataHeader dataHeader;
730 if(sizeof dataHeader != file.read(reinterpret_cast<char*>(&dataHeader), sizeof dataHeader)) {
731 kDebug(264) << "Unable to read from data file" << mDataFile;
732 return false;
733 }
734
735 mDataMmapInfo.size = dataHeader.size;
736 return true;
737}
738
739bool KPixmapCache::Private::loadIndexHeader()
740{
741 // Open file and datastream on it
742 QFile file(mIndexFile);
743 if (!file.open(QIODevice::ReadOnly)) {
744 return false;
745 }
746
747 KPixmapCacheIndexHeader indexHeader;
748 if(sizeof indexHeader != file.read(reinterpret_cast<char*>(&indexHeader), sizeof indexHeader)) {
749 kWarning(264) << "Failed to read index file's header";
750 q->recreateCacheFiles();
751 return false;
752 }
753
754 mCacheId = indexHeader.cacheId;
755 mTimestamp = indexHeader.timestamp;
756 mIndexMmapInfo.size = indexHeader.size;
757
758 QDataStream stream(&file);
759
760 // Give custom implementations chance to load their headers
761 if (!q->loadCustomIndexHeader(stream)) {
762 return false;
763 }
764
765 mHeaderSize = file.pos();
766 mIndexRootOffset = file.pos();
767
768 return true;
769}
770
771QString KPixmapCache::Private::indexKey(const QString& key)
772{
773 const QByteArray latin1 = key.toLatin1();
774 return QString("%1%2").arg((ushort)qChecksum(latin1.data(), latin1.size()), 4, 16, QLatin1Char('0')).arg(key);
775}
776
777
778QString KPixmapCache::Private::qpcKey(const QString& key) const
779{
780 return mThisString + key;
781}
782
783void KPixmapCache::Private::writeIndexEntry(QDataStream& stream, const QString& key, int dataoffset)
784{
785 // New entry will be written to the end of the file
786 qint32 offset = stream.device()->size();
787 // Find parent index node for this node.
788 int parentoffset = binarySearchKey(stream, key, mIndexRootOffset);
789 if (parentoffset != stream.device()->size()) {
790 // Check if this entry has the same key
791 QString fkey;
792 stream.device()->seek(parentoffset);
793 stream >> fkey;
794
795 // The key would be empty if the cache had been discarded.
796 if (key == fkey || fkey.isEmpty()) {
797 // We're overwriting an existing entry
798 offset = parentoffset;
799 }
800 }
801
802 stream.device()->seek(offset);
803 // Write the data
804 stream << key << (qint32)dataoffset;
805 // Statistics (# of uses and last used timestamp)
806 stream << (quint32)1 << (quint32)::time(0);
807 // Write (empty) children offsets
808 stream << (qint32)0 << (qint32)0;
809
810 // If we created the root node or overwrote existing entry then the two
811 // offsets are equal and we're done. Otherwise set parent's child offset
812 // to correct value.
813 if (parentoffset != offset) {
814 stream.device()->seek(parentoffset);
815 QString fkey;
816 qint32 foffset, tmp;
817 quint32 timesused, lastused;
818 stream >> fkey >> foffset >> timesused >> lastused;
819 if (key < fkey) {
820 // New entry will be parent's left child
821 stream << offset;
822 } else {
823 // New entry will be parent's right child
824 stream >> tmp;
825 stream << offset;
826 }
827 }
828}
829
830bool KPixmapCache::Private::removeEntries(int newsize)
831{
832 KPCLockFile lock(mLockFileName);
833 if (!lock.isValid()) {
834 kDebug(264) << "Couldn't lock cache" << mName;
835 return false;
836 }
837 QMutexLocker mutexlocker(&mMutex);
838
839 // Open old (current) files
840 QFile indexfile(mIndexFile);
841 if (!indexfile.open(QIODevice::ReadOnly)) {
842 kDebug(264) << "Couldn't open old index file";
843 return false;
844 }
845 QDataStream istream(&indexfile);
846 QFile datafile(mDataFile);
847 if (!datafile.open(QIODevice::ReadOnly)) {
848 kDebug(264) << "Couldn't open old data file";
849 return false;
850 }
851 if (datafile.size() <= newsize*1024) {
852 kDebug(264) << "Cache size is already within limit (" << datafile.size() << " <= " << newsize*1024 << ")";
853 return true;
854 }
855 QDataStream dstream(&datafile);
856 // Open new files
857 QFile newindexfile(mIndexFile + ".new");
858 if (!newindexfile.open(QIODevice::ReadWrite)) {
859 kDebug(264) << "Couldn't open new index file";
860 return false;
861 }
862 QDataStream newistream(&newindexfile);
863 QFile newdatafile(mDataFile + ".new");
864 if (!newdatafile.open(QIODevice::WriteOnly)) {
865 kDebug(264) << "Couldn't open new data file";
866 return false;
867 }
868 QDataStream newdstream(&newdatafile);
869
870 // Copy index file header
871 char* header = new char[mHeaderSize];
872 if (istream.readRawData(header, mHeaderSize) != (int)mHeaderSize) {
873 kDebug(264) << "Couldn't read index header";
874 delete [] header;
875 return false;
876 }
877
878 // Set file size to 0 for mmap stuff
879 reinterpret_cast<KPixmapCacheIndexHeader *>(header)->size = 0;
880 newistream.writeRawData(header, mHeaderSize);
881
882 // Copy data file header
883 int dataheaderlen = sizeof(KPixmapCacheDataHeader);
884
885 // mHeaderSize is always bigger than dataheaderlen, so we needn't create
886 // new buffer
887 if (dstream.readRawData(header, dataheaderlen) != dataheaderlen) {
888 kDebug(264) << "Couldn't read data header";
889 delete [] header;
890 return false;
891 }
892
893 // Set file size to 0 for mmap stuff
894 reinterpret_cast<KPixmapCacheDataHeader *>(header)->size = 0;
895 newdstream.writeRawData(header, dataheaderlen);
896 delete [] header;
897
898 // Load all entries
899 QList<KPixmapCacheEntry> entries;
900 // Do BFS to find all entries
901 QQueue<int> open;
902 open.enqueue(mIndexRootOffset);
903 while (!open.isEmpty()) {
904 int indexoffset = open.dequeue();
905 indexfile.seek(indexoffset);
906 QString fkey;
907 qint32 foffset;
908 quint32 timesused, lastused;
909 qint32 leftchild, rightchild;
910 istream >> fkey >> foffset >> timesused >> lastused >> leftchild >> rightchild;
911 entries.append(KPixmapCacheEntry(indexoffset, fkey, foffset, entries.count(), timesused, lastused));
912 if (leftchild) {
913 open.enqueue(leftchild);
914 }
915 if (rightchild) {
916 open.enqueue(rightchild);
917 }
918 }
919
920 // Sort the entries according to RemoveStrategy. This moves the best
921 // entries to the beginning of the list
922 if (q->removeEntryStrategy() == RemoveOldest) {
923 qSort(entries.begin(), entries.end(), compareEntriesByAge);
924 } else if (q->removeEntryStrategy() == RemoveSeldomUsed) {
925 qSort(entries.begin(), entries.end(), compareEntriesByTimesUsed);
926 } else {
927 qSort(entries.begin(), entries.end(), compareEntriesByLastUsed);
928 }
929
930 // Write some entries to the new files
931 int entrieswritten = 0;
932 for (entrieswritten = 0; entrieswritten < entries.count(); entrieswritten++) {
933 const KPixmapCacheEntry& entry = entries[entrieswritten];
934 // Load data
935 datafile.seek(entry.dataoffset);
936 int entrysize = -datafile.pos();
937 // We have some duplication here but this way we avoid uncompressing
938 // the data and constructing QPixmap which we don't really need.
939 QString fkey;
940 dstream >> fkey;
941 qint32 format, w, h, bpl;
942 dstream >> format >> w >> h >> bpl;
943 QByteArray imgdatacompressed;
944 dstream >> imgdatacompressed;
945 // Load custom data as well. This will be stored by the subclass itself.
946 if (!q->loadCustomData(dstream)) {
947 return false;
948 }
949 // Find out size of this entry
950 entrysize += datafile.pos();
951
952 // Make sure we'll stay within size limit
953 if (newdatafile.size() + entrysize > newsize*1024) {
954 break;
955 }
956
957 // Now write the same data to the new file
958 int newdataoffset = newdatafile.pos();
959 newdstream << fkey;
960 newdstream << format << w << h << bpl;
961 newdstream << imgdatacompressed;
962 q->writeCustomData(newdstream);
963
964 // Finally, add the index entry
965 writeIndexEntry(newistream, entry.key, newdataoffset);
966 }
967
968 // Remove old files and rename the new ones
969 indexfile.remove();
970 datafile.remove();
971 newindexfile.rename(mIndexFile);
972 newdatafile.rename(mDataFile);
973 invalidateMmapFiles();
974
975 kDebug(264) << "Wrote back" << entrieswritten << "of" << entries.count() << "entries";
976
977 return true;
978}
979
980
981
982
983KPixmapCache::KPixmapCache(const QString& name)
984 :d(new Private(this))
985{
986 d->mName = name;
987 d->mUseQPixmapCache = true;
988 d->mCacheLimit = 3 * 1024;
989 d->mRemoveStrategy = RemoveLeastRecentlyUsed;
990
991 // We cannot call init() here because the subclasses haven't been
992 // constructed yet and so their virtual methods cannot be used.
993 d->mInited = false;
994}
995
996KPixmapCache::~KPixmapCache()
997{
998 d->unmmapFiles();
999 delete d;
1000}
1001
1002void KPixmapCache::Private::init()
1003{
1004 mInited = true;
1005
1006#ifdef DISABLE_PIXMAPCACHE
1007 mValid = mEnabled = false;
1008#else
1009 mValid = false;
1010
1011 // Find locations of the files
1012 mIndexFile = KGlobal::dirs()->locateLocal("cache", "kpc/" + mName + ".index");
1013 mDataFile = KGlobal::dirs()->locateLocal("cache", "kpc/" + mName + ".data");
1014 mLockFileName = KGlobal::dirs()->locateLocal("cache", "kpc/" + mName + ".lock");
1015
1016 mEnabled = true;
1017 mEnabled &= checkLockFile();
1018 mEnabled &= checkFileVersion(mDataFile);
1019 mEnabled &= checkFileVersion(mIndexFile);
1020 if (!mEnabled) {
1021 kDebug(264) << "Pixmap cache" << mName << "is disabled";
1022 } else {
1023 // Cache is enabled, but check if it's ready for use
1024 loadDataHeader();
1025 q->setValid(loadIndexHeader());
1026 // Init mmap stuff if mmap is used
1027 mmapFiles();
1028 }
1029#endif
1030}
1031
1032void KPixmapCache::ensureInited() const
1033{
1034 if (!d->mInited) {
1035 const_cast<KPixmapCache*>(this)->d->init();
1036 }
1037}
1038
1039bool KPixmapCache::loadCustomIndexHeader(QDataStream&)
1040{
1041 return true;
1042}
1043
1044void KPixmapCache::writeCustomIndexHeader(QDataStream&)
1045{
1046}
1047
1048bool KPixmapCache::isEnabled() const
1049{
1050 ensureInited();
1051 return d->mEnabled;
1052}
1053
1054bool KPixmapCache::isValid() const
1055{
1056 ensureInited();
1057 return d->mEnabled && d->mValid;
1058}
1059
1060void KPixmapCache::setValid(bool valid)
1061{
1062 ensureInited();
1063 d->mValid = valid;
1064}
1065
1066unsigned int KPixmapCache::timestamp() const
1067{
1068 ensureInited();
1069 return d->mTimestamp;
1070}
1071
1072void KPixmapCache::setTimestamp(unsigned int ts)
1073{
1074 ensureInited();
1075 d->mTimestamp = ts;
1076
1077 // Write to file
1078 KPCLockFile lock(d->mLockFileName);
1079 if (!lock.isValid()) {
1080 // FIXME: now what?
1081 return;
1082 }
1083
1084 QIODevice* device = d->indexDevice();
1085 if (!device) {
1086 return;
1087 }
1088
1089 KPixmapCacheIndexHeader header;
1090 device->seek(0);
1091 if(sizeof header != device->read(reinterpret_cast<char*>(&header), sizeof header)) {
1092 delete device;
1093 return;
1094 }
1095
1096 header.timestamp = ts;
1097 device->seek(0);
1098 device->write(reinterpret_cast<char *>(&header), sizeof header);
1099
1100 delete device;
1101}
1102
1103int KPixmapCache::size() const
1104{
1105 ensureInited();
1106 if (d->mDataMmapInfo.file) {
1107 return d->mDataMmapInfo.size / 1024;
1108 }
1109 return QFileInfo(d->mDataFile).size() / 1024;
1110}
1111
1112void KPixmapCache::setUseQPixmapCache(bool use)
1113{
1114 d->mUseQPixmapCache = use;
1115}
1116
1117bool KPixmapCache::useQPixmapCache() const
1118{
1119 return d->mUseQPixmapCache;
1120}
1121
1122int KPixmapCache::cacheLimit() const
1123{
1124 return d->mCacheLimit;
1125}
1126
1127void KPixmapCache::setCacheLimit(int kbytes)
1128{
1129 //FIXME: KDE5: this should be uint!
1130 if (kbytes < 0) {
1131 return;
1132 }
1133
1134 d->mCacheLimit = kbytes;
1135
1136 // if we are initialized, let's make sure that we are actually within
1137 // our limits.
1138 if (d->mInited && d->mCacheLimit && size() > d->mCacheLimit) {
1139 if (size() > (int)(d->mCacheLimit)) {
1140 // Can't wait any longer, do it immediately
1141 d->removeEntries(d->mCacheLimit * 0.65);
1142 }
1143 }
1144}
1145
1146KPixmapCache::RemoveStrategy KPixmapCache::removeEntryStrategy() const
1147{
1148 return d->mRemoveStrategy;
1149}
1150
1151void KPixmapCache::setRemoveEntryStrategy(KPixmapCache::RemoveStrategy strategy)
1152{
1153 d->mRemoveStrategy = strategy;
1154}
1155
1156bool KPixmapCache::recreateCacheFiles()
1157{
1158 if (!isEnabled()) {
1159 return false;
1160 }
1161
1162 KPCLockFile lock(d->mLockFileName);
1163 // Hope we got the lock...
1164
1165 d->invalidateMmapFiles();
1166 d->mEnabled = false;
1167
1168 // Create index file
1169 KSaveFile indexfile(d->mIndexFile);
1170 if (!indexfile.open(QIODevice::WriteOnly)) {
1171 kError() << "Couldn't create index file" << d->mIndexFile;
1172 return false;
1173 }
1174
1175 d->mCacheId = ::time(0);
1176 d->mTimestamp = ::time(0);
1177
1178 // We can't know the full size until custom headers written.
1179 // mmapFiles() will take care of correcting the size.
1180 KPixmapCacheIndexHeader indexHeader = { {0}, KPIXMAPCACHE_VERSION, 0, d->mCacheId, d->mTimestamp };
1181 memcpy(indexHeader.magic, KPC_MAGIC, sizeof(indexHeader.magic));
1182
1183 indexfile.write(reinterpret_cast<char*>(&indexHeader), sizeof indexHeader);
1184
1185 // Create data file
1186 KSaveFile datafile(d->mDataFile);
1187 if (!datafile.open(QIODevice::WriteOnly)) {
1188 kError() << "Couldn't create data file" << d->mDataFile;
1189 return false;
1190 }
1191
1192 KPixmapCacheDataHeader dataHeader = { {0}, KPIXMAPCACHE_VERSION, sizeof dataHeader };
1193 memcpy(dataHeader.magic, KPC_MAGIC, sizeof(dataHeader.magic));
1194
1195 datafile.write(reinterpret_cast<char*>(&dataHeader), sizeof dataHeader);
1196
1197 setValid(true);
1198
1199 QDataStream istream(&indexfile);
1200 writeCustomIndexHeader(istream);
1201 d->mHeaderSize = indexfile.pos();
1202
1203 d->mIndexRootOffset = d->mHeaderSize;
1204
1205 // Close the files and mmap them (if mmapping is used)
1206 indexfile.close();
1207 datafile.close();
1208 indexfile.finalize();
1209 datafile.finalize();
1210
1211 d->mEnabled = true;
1212 d->mmapFiles();
1213
1214 return true;
1215}
1216
1217void KPixmapCache::deleteCache(const QString& name)
1218{
1219 QString indexFile = KGlobal::dirs()->locateLocal("cache", "kpc/" + name + ".index");
1220 QString dataFile = KGlobal::dirs()->locateLocal("cache", "kpc/" + name + ".data");
1221
1222 QFile::remove(indexFile);
1223 QFile::remove(dataFile);
1224}
1225
1226void KPixmapCache::discard()
1227{
1228 // To "discard" the cache we simply have to make sure that every that
1229 // was in there before is no longer present when we search for them.
1230 // Easiest way to do *that* is to simply delete the index.
1231
1232 KPCLockFile lock(d->mLockFileName);
1233 if(!lock.isValid()) {
1234 kError(264) << "Unable to lock pixmap cache when trying to discard it";
1235 return;
1236 }
1237
1238 QIODevice *device = d->indexDevice();
1239 if (!device) {
1240 kError(264) << "Unable to access index when trying to discard cache";
1241 return;
1242 }
1243
1244 device->seek(d->mIndexRootOffset);
1245 QDataStream stream(device);
1246
1247 // Stream an empty QString as the hash key to signify that the cache
1248 // has been discarded.
1249 stream << QString();
1250
1251 if (d->mUseQPixmapCache) {
1252 // TODO: This is broken, it removes every cached QPixmap in the whole
1253 // process, not just this KPixmapCache.
1254 QPixmapCache::clear();
1255 }
1256}
1257
1258void KPixmapCache::removeEntries(int newsize)
1259{
1260 if (!newsize) {
1261 newsize = d->mCacheLimit;
1262
1263 if (!newsize) {
1264 // nothing to do!
1265 return;
1266 }
1267 }
1268
1269 d->removeEntries(newsize);
1270}
1271
1272bool KPixmapCache::find(const QString& key, QPixmap& pix)
1273{
1274 ensureInited();
1275 if (!isValid()) {
1276 return false;
1277 }
1278
1279 //kDebug(264) << "key:" << key << ", use QPC:" << d->mUseQPixmapCache;
1280 // First try the QPixmapCache
1281 if (d->mUseQPixmapCache && QPixmapCache::find(d->qpcKey(key), &pix)) {
1282 //kDebug(264) << "Found from QPC";
1283 return true;
1284 }
1285
1286 KPCLockFile lock(d->mLockFileName);
1287 if (!lock.isValid()) {
1288 return false;
1289 }
1290
1291 // Try to find the offset
1292 QString indexkey = d->indexKey(key);
1293 int offset = d->findOffset(indexkey);
1294 //kDebug(264) << "found offset" << offset;
1295 if (offset == -1) {
1296 return false;
1297 }
1298
1299 // Load the data
1300 bool ret = d->loadData(offset, pix);
1301 if (ret && d->mUseQPixmapCache) {
1302 // This pixmap wasn't in QPC, put it there
1303 QPixmapCache::insert(d->qpcKey(key), pix);
1304 }
1305 return ret;
1306}
1307
1308bool KPixmapCache::Private::loadData(int offset, QPixmap& pix)
1309{
1310 // Open device and datastream on it
1311 QIODevice* device = dataDevice();
1312 if (!device) {
1313 return false;
1314 }
1315 //kDebug(264) << "Seeking to pos" << offset << "/" << file.size();
1316 if (!device->seek(offset)) {
1317 kError() << "Couldn't seek to pos" << offset;
1318 delete device;
1319 return false;
1320 }
1321 QDataStream stream(device);
1322
1323 // Load
1324 QString fkey;
1325 stream >> fkey;
1326
1327 // Load image info and compressed data
1328 qint32 format, w, h, bpl;
1329 stream >> format >> w >> h >> bpl;
1330 QByteArray imgdatacompressed;
1331 stream >> imgdatacompressed;
1332
1333 // Uncompress the data and create the image
1334 // TODO: make sure this actually works. QImage ctor we use here seems to
1335 // want 32-bit aligned data. QByteArray uses malloc() to allocate it's
1336 // data, which _probably_ returns 32-bit aligned data.
1337 QByteArray imgdata = qUncompress(imgdatacompressed);
1338 if (!imgdata.isEmpty()) {
1339 QImage img((const uchar*)imgdata.constData(), w, h, bpl, (QImage::Format)format);
1340 img.bits(); // make deep copy since we don't want to keep imgdata around
1341 pix = QPixmap::fromImage(img);
1342 } else {
1343 pix = QPixmap(w, h);
1344 }
1345
1346 if (!q->loadCustomData(stream)) {
1347 delete device;
1348 return false;
1349 }
1350
1351 delete device;
1352 if (stream.status() != QDataStream::Ok) {
1353 kError() << "stream is bad :-( status=" << stream.status();
1354 return false;
1355 }
1356
1357 //kDebug(264) << "pixmap successfully loaded";
1358 return true;
1359}
1360
1361bool KPixmapCache::loadCustomData(QDataStream&)
1362{
1363 return true;
1364}
1365
1366void KPixmapCache::insert(const QString& key, const QPixmap& pix)
1367{
1368 ensureInited();
1369 if (!isValid()) {
1370 return;
1371 }
1372
1373 //kDebug(264) << "key:" << key << ", size:" << pix.width() << "x" << pix.height();
1374 // Insert to QPixmapCache as well
1375 if (d->mUseQPixmapCache) {
1376 QPixmapCache::insert(d->qpcKey(key), pix);
1377 }
1378
1379 KPCLockFile lock(d->mLockFileName);
1380 if (!lock.isValid()) {
1381 return;
1382 }
1383
1384 // Insert to cache
1385 QString indexkey = d->indexKey(key);
1386 int offset = d->writeData(key, pix);
1387 //kDebug(264) << "data is at offset" << offset;
1388 if (offset == -1) {
1389 return;
1390 }
1391
1392 d->writeIndex(indexkey, offset);
1393
1394 // Make sure the cache size stays within limits
1395 if (d->mCacheLimit && size() > d->mCacheLimit) {
1396 lock.unlock();
1397 if (size() > (int)(d->mCacheLimit)) {
1398 // Can't wait any longer, do it immediately
1399 d->removeEntries(d->mCacheLimit * 0.65);
1400 }
1401 }
1402}
1403
1404int KPixmapCache::Private::writeData(const QString& key, const QPixmap& pix)
1405{
1406 // Open device and datastream on it
1407 QIODevice* device = dataDevice();
1408 if (!device) {
1409 return -1;
1410 }
1411 int offset = device->size();
1412 device->seek(offset);
1413 QDataStream stream(device);
1414
1415 // Write the data
1416 stream << key;
1417 // Write image info and compressed data
1418 QImage img = pix.toImage();
1419 QByteArray imgdatacompressed = qCompress(img.bits(), img.numBytes());
1420 stream << (qint32)img.format() << (qint32)img.width() << (qint32)img.height() << (qint32)img.bytesPerLine();
1421 stream << imgdatacompressed;
1422
1423 q->writeCustomData(stream);
1424
1425 delete device;
1426 return offset;
1427}
1428
1429bool KPixmapCache::writeCustomData(QDataStream&)
1430{
1431 return true;
1432}
1433
1434void KPixmapCache::Private::writeIndex(const QString& key, int dataoffset)
1435{
1436 // Open device and datastream on it
1437 QIODevice* device = indexDevice();
1438 if (!device) {
1439 return;
1440 }
1441 QDataStream stream(device);
1442
1443 writeIndexEntry(stream, key, dataoffset);
1444 delete device;
1445}
1446
1447QPixmap KPixmapCache::loadFromFile(const QString& filename)
1448{
1449 QFileInfo fi(filename);
1450 if (!fi.exists()) {
1451 return QPixmap();
1452 } else if (fi.lastModified().toTime_t() > timestamp()) {
1453 // Cache is obsolete, will be regenerated
1454 discard();
1455 }
1456
1457 QPixmap pix;
1458 QString key("file:" + filename);
1459 if (!find(key, pix)) {
1460 // It wasn't in the cache, so load it...
1461 pix = QPixmap(filename);
1462 if (pix.isNull()) {
1463 return pix;
1464 }
1465 // ... and put it there
1466 insert(key, pix);
1467 }
1468
1469 return pix;
1470}
1471
1472#ifndef _WIN32_WCE
1473QPixmap KPixmapCache::loadFromSvg(const QString& filename, const QSize& size)
1474{
1475 QFileInfo fi(filename);
1476 if (!fi.exists()) {
1477 return QPixmap();
1478 } else if (fi.lastModified().toTime_t() > timestamp()) {
1479 // Cache is obsolete, will be regenerated
1480 discard();
1481 }
1482
1483 QPixmap pix;
1484 QString key = QString("file:%1_%2_%3").arg(filename).arg(size.width()).arg(size.height());
1485 if (!find(key, pix)) {
1486 // It wasn't in the cache, so load it...
1487 QSvgRenderer svg;
1488 if (!svg.load(filename)) {
1489 return pix; // null pixmap
1490 } else {
1491 QSize pixSize = size.isValid() ? size : svg.defaultSize();
1492 pix = QPixmap(pixSize);
1493 pix.fill(Qt::transparent);
1494
1495 QPainter p(&pix);
1496 svg.render(&p, QRectF(QPointF(), pixSize));
1497 }
1498
1499 // ... and put it there
1500 insert(key, pix);
1501 }
1502
1503 return pix;
1504}
1505#endif
1506
KLockFile
KLockFile::LockResult
LockResult
KLockFile::LockOK
LockOK
KLockFile::NoBlockFlag
NoBlockFlag
KPixmapCache
General-purpose pixmap cache for KDE.
Definition: kpixmapcache.h:86
KPixmapCache::writeCustomData
virtual bool writeCustomData(QDataStream &stream)
Can be used by subclasses to write custom data into the stream.
Definition: kpixmapcache.cpp:1429
KPixmapCache::~KPixmapCache
virtual ~KPixmapCache()
Definition: kpixmapcache.cpp:996
KPixmapCache::isEnabled
bool isEnabled() const
Cache will be disabled when e.g.
Definition: kpixmapcache.cpp:1048
KPixmapCache::insert
virtual void insert(const QString &key, const QPixmap &pix)
Inserts the pixmap pix into the cache, associated with the key key.
Definition: kpixmapcache.cpp:1366
KPixmapCache::isValid
bool isValid() const
Definition: kpixmapcache.cpp:1054
KPixmapCache::KPixmapCache
KPixmapCache(const QString &name)
Constucts the pixmap cache object.
Definition: kpixmapcache.cpp:983
KPixmapCache::loadCustomData
virtual bool loadCustomData(QDataStream &stream)
Can be used by subclasses to load custom data from the stream.
Definition: kpixmapcache.cpp:1361
KPixmapCache::setCacheLimit
void setCacheLimit(int kbytes)
Sets the maximum size of the cache (in kilobytes).
Definition: kpixmapcache.cpp:1127
KPixmapCache::deleteCache
static void deleteCache(const QString &name)
Deletes a pixmap cache.
Definition: kpixmapcache.cpp:1217
KPixmapCache::setRemoveEntryStrategy
void setRemoveEntryStrategy(RemoveStrategy strategy)
Sets the removeEntryStrategy used when removing entries.
Definition: kpixmapcache.cpp:1151
KPixmapCache::setValid
void setValid(bool valid)
Sets whether this cache is valid or not.
Definition: kpixmapcache.cpp:1060
KPixmapCache::writeCustomIndexHeader
virtual void writeCustomIndexHeader(QDataStream &stream)
Can be used by subclasses to write custom data into cache's header.
Definition: kpixmapcache.cpp:1044
KPixmapCache::timestamp
unsigned int timestamp() const
Definition: kpixmapcache.cpp:1066
KPixmapCache::find
virtual bool find(const QString &key, QPixmap &pix)
Tries to load pixmap with the specified key from cache.
Definition: kpixmapcache.cpp:1272
KPixmapCache::loadFromSvg
QPixmap loadFromSvg(const QString &filename, const QSize &size=QSize())
Same as loadFromFile(), but using an SVG file instead.
Definition: kpixmapcache.cpp:1473
KPixmapCache::useQPixmapCache
bool useQPixmapCache() const
Whether QPixmapCache should be used to cache pixmaps in memory in addition to caching them on the dis...
Definition: kpixmapcache.cpp:1117
KPixmapCache::size
int size() const
Definition: kpixmapcache.cpp:1103
KPixmapCache::setTimestamp
void setTimestamp(unsigned int time)
Sets the timestamp of app-specific cache.
Definition: kpixmapcache.cpp:1072
KPixmapCache::removeEntryStrategy
RemoveStrategy removeEntryStrategy() const
Definition: kpixmapcache.cpp:1146
KPixmapCache::RemoveStrategy
RemoveStrategy
Describes which entries will be removed first during cache cleanup.
Definition: kpixmapcache.h:216
KPixmapCache::RemoveLeastRecentlyUsed
@ RemoveLeastRecentlyUsed
least recently used entries are removed first.
Definition: kpixmapcache.h:221
KPixmapCache::removeEntries
void removeEntries(int newsize=0)
Removes some of the entries in the cache according to current removeEntryStrategy().
Definition: kpixmapcache.cpp:1258
KPixmapCache::recreateCacheFiles
bool recreateCacheFiles()
This function causes the cache files to be recreate by invalidating the cache.
Definition: kpixmapcache.cpp:1156
KPixmapCache::loadCustomIndexHeader
virtual bool loadCustomIndexHeader(QDataStream &stream)
Can be used by subclasses to load custom data from cache's header.
Definition: kpixmapcache.cpp:1039
KPixmapCache::setUseQPixmapCache
void setUseQPixmapCache(bool use)
Sets whether QPixmapCache (memory caching) should be used in addition to disk cache.
Definition: kpixmapcache.cpp:1112
KPixmapCache::loadFromFile
QPixmap loadFromFile(const QString &filename)
Loads a pixmap from given file, using the cache.
Definition: kpixmapcache.cpp:1447
KPixmapCache::discard
void discard()
Deletes all entries and reinitializes this cache.
Definition: kpixmapcache.cpp:1226
KPixmapCache::ensureInited
void ensureInited() const
Makes sure that the cache is initialized correctly, including the loading of the cache index and data...
Definition: kpixmapcache.cpp:1032
KPixmapCache::cacheLimit
int cacheLimit() const
Definition: kpixmapcache.cpp:1122
KSaveFile
KSaveFile::finalize
bool finalize()
KSaveFile::open
virtual bool open(OpenMode flags=QIODevice::ReadWrite)
KStandardDirs::locateLocal
static QString locateLocal(const char *type, const QString &filename, bool createDir, const KComponentData &cData=KGlobal::mainComponent())
QIODevice
QList
QSvgRenderer
header
const char header[]
kDebug
#define kDebug
kWarning
#define kWarning
kdebug.h
kglobal.h
klockfile.h
madvise
int madvise(caddr_t addr, size_t len, int advice)
KPIXMAPCACHE_VERSION
#define KPIXMAPCACHE_VERSION
Definition: kpixmapcache.cpp:67
kpixmapcache.h
ksavefile.h
kstandarddirs.h
ksvgrenderer.h
open
int open(const QString &pathname, int flags, mode_t mode)
KGlobal::dirs
KStandardDirs * dirs()
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Feb 20 2023 00:00:00 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDEUI

Skip menu "KDEUI"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Modules
  • Related Pages

kdelibs-4.14.38 API Reference

Skip menu "kdelibs-4.14.38 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal