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

KDEUI

  • sources
  • kde-4.14
  • kdelibs
  • 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
63 extern "C" int madvise(caddr_t addr, size_t len, int advice);
64 #endif
65 #endif
66 
67 #define KPIXMAPCACHE_VERSION 0x000208
68 
69 namespace {
70 
71 class KPCLockFile
72 {
73 public:
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 
109 private:
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.)
121 static const char KPC_MAGIC[] = "KDE PIXMAP CACHE DEUX";
122 struct 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 
131 struct 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 
144 class KPCMemoryDevice : public QIODevice
145 {
146 public:
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 
154 protected:
155  virtual qint64 readData(char* data, qint64 maxSize);
156  virtual qint64 writeData(const char* data, qint64 maxSize);
157 
158 private:
159  char* mMemory;
160  KPixmapCacheIndexHeader *mHeader; // alias of mMemory
161  quint32* mSize;
162  quint32 mInitialSize;
163  qint64 mAvailable;
164  quint32 mPos;
165 };
166 
167 KPCMemoryDevice::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 
183 KPCMemoryDevice::~KPCMemoryDevice()
184 {
185  if (*mSize != mInitialSize) {
186  // Update file size
187  mHeader->size = *mSize;
188  }
189 }
190 
191 bool 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 
200 qint64 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 
211 qint64 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 
226 class KPixmapCache::Private
227 {
228 public:
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.
356 QList<KPixmapCache::Private *> KPixmapCache::Private::mCaches;
357 
358 unsigned KPixmapCache::Private::kpcNumber = 0;
359 
360 KPixmapCache::Private::Private(KPixmapCache* _q)
361 {
362  q = _q;
363  mCaches.append(this);
364  mThisString = QString("%1").arg(kpcNumber++);
365 }
366 
367 KPixmapCache::Private::~Private()
368 {
369  mCaches.removeAll(this);
370 }
371 
372 bool 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 
395 void KPixmapCache::Private::unmmapFiles()
396 {
397  unmmapFile(&mIndexMmapInfo);
398  unmmapFile(&mDataMmapInfo);
399 }
400 
401 void 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 
412 bool 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 
463 void 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 
477 QIODevice* 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 
546 QIODevice* 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 
588 int 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 
615 int 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 
666 bool 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 
678 bool 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 
721 bool 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 
739 bool 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 
771 QString 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 
778 QString KPixmapCache::Private::qpcKey(const QString& key) const
779 {
780  return mThisString + key;
781 }
782 
783 void 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 
830 bool 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 
983 KPixmapCache::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 
996 KPixmapCache::~KPixmapCache()
997 {
998  d->unmmapFiles();
999  delete d;
1000 }
1001 
1002 void 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 
1032 void KPixmapCache::ensureInited() const
1033 {
1034  if (!d->mInited) {
1035  const_cast<KPixmapCache*>(this)->d->init();
1036  }
1037 }
1038 
1039 bool KPixmapCache::loadCustomIndexHeader(QDataStream&)
1040 {
1041  return true;
1042 }
1043 
1044 void KPixmapCache::writeCustomIndexHeader(QDataStream&)
1045 {
1046 }
1047 
1048 bool KPixmapCache::isEnabled() const
1049 {
1050  ensureInited();
1051  return d->mEnabled;
1052 }
1053 
1054 bool KPixmapCache::isValid() const
1055 {
1056  ensureInited();
1057  return d->mEnabled && d->mValid;
1058 }
1059 
1060 void KPixmapCache::setValid(bool valid)
1061 {
1062  ensureInited();
1063  d->mValid = valid;
1064 }
1065 
1066 unsigned int KPixmapCache::timestamp() const
1067 {
1068  ensureInited();
1069  return d->mTimestamp;
1070 }
1071 
1072 void 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 
1103 int 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 
1112 void KPixmapCache::setUseQPixmapCache(bool use)
1113 {
1114  d->mUseQPixmapCache = use;
1115 }
1116 
1117 bool KPixmapCache::useQPixmapCache() const
1118 {
1119  return d->mUseQPixmapCache;
1120 }
1121 
1122 int KPixmapCache::cacheLimit() const
1123 {
1124  return d->mCacheLimit;
1125 }
1126 
1127 void 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 
1146 KPixmapCache::RemoveStrategy KPixmapCache::removeEntryStrategy() const
1147 {
1148  return d->mRemoveStrategy;
1149 }
1150 
1151 void KPixmapCache::setRemoveEntryStrategy(KPixmapCache::RemoveStrategy strategy)
1152 {
1153  d->mRemoveStrategy = strategy;
1154 }
1155 
1156 bool 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 
1217 void 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 
1226 void 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 
1258 void 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 
1272 bool 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 
1308 bool 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 
1361 bool KPixmapCache::loadCustomData(QDataStream&)
1362 {
1363  return true;
1364 }
1365 
1366 void 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 
1404 int 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 
1429 bool KPixmapCache::writeCustomData(QDataStream&)
1430 {
1431  return true;
1432 }
1433 
1434 void 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 
1447 QPixmap 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
1473 QPixmap 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 
QIODevice
QMutex
qint64
QSize::isValid
bool isValid() const
KPixmapCache::isValid
bool isValid() const
Definition: kpixmapcache.cpp:1054
header
const char header[]
KPixmapCache::setValid
void setValid(bool valid)
Sets whether this cache is valid or not.
Definition: kpixmapcache.cpp:1060
QSize::width
int width() const
QQueue
kdebug.h
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
QPixmap::fill
void fill(const QColor &color)
QSvgRenderer::render
void render(QPainter *painter)
QIODevice::seek
virtual bool seek(qint64 pos)
QByteArray
QQueue::enqueue
void enqueue(const T &t)
QFile::remove
bool remove()
QDataStream
QQueue::dequeue
T dequeue()
ksavefile.h
QFile::pos
virtual qint64 pos() const
QSvgRenderer
KPixmapCache::loadCustomData
virtual bool loadCustomData(QDataStream &stream)
Can be used by subclasses to load custom data from the stream.
Definition: kpixmapcache.cpp:1361
KSaveFile
KSaveFile::open
virtual bool open(OpenMode flags=QIODevice::ReadWrite)
KGlobal::dirs
KStandardDirs * dirs()
QByteArray::isEmpty
bool isEmpty() const
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
KStandardAction::name
const char * name(StandardAction id)
This will return the internal name of a given standard action.
Definition: kstandardaction.cpp:223
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::timestamp
unsigned int timestamp() const
Definition: kpixmapcache.cpp:1066
kError
static QDebug kError(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
QPixmap::fromImage
QPixmap fromImage(const QImage &image, QFlags< Qt::ImageConversionFlag > flags)
KPixmapCache
General-purpose pixmap cache for KDE.
Definition: kpixmapcache.h:85
KPixmapCache::RemoveLeastRecentlyUsed
least recently used entries are removed first.
Definition: kpixmapcache.h:221
quint32
KLockFile::LockResult
LockResult
QFile::exists
bool exists() const
KPixmapCache::loadFromFile
QPixmap loadFromFile(const QString &filename)
Loads a pixmap from given file, using the cache.
Definition: kpixmapcache.cpp:1447
QIODevice::readData
virtual qint64 readData(char *data, qint64 maxSize)=0
kDebug
static QDebug kDebug(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
QFile
QSvgRenderer::defaultSize
QSize defaultSize() const
QIODevice::pos
virtual qint64 pos() const
QPointF
KPixmapCache::recreateCacheFiles
bool recreateCacheFiles()
This function causes the cache files to be recreate by invalidating the cache.
Definition: kpixmapcache.cpp:1156
open
int open(const QString &pathname, int flags, mode_t mode)
KPixmapCache::writeCustomIndexHeader
virtual void writeCustomIndexHeader(QDataStream &stream)
Can be used by subclasses to write custom data into cache's header.
Definition: kpixmapcache.cpp:1044
KLockFile
KStandardGuiItem::Ok
Definition: kstandardguiitem.h:50
KPixmapCache::KPixmapCache
KPixmapCache(const QString &name)
Constucts the pixmap cache object.
Definition: kpixmapcache.cpp:983
klockfile.h
kglobal.h
KPixmapCache::find
virtual bool find(const QString &key, QPixmap &pix)
Tries to load pixmap with the specified key from cache.
Definition: kpixmapcache.cpp:1272
QList::count
int count(const T &value) const
QList::append
void append(const T &value)
QSvgRenderer::load
bool load(const QString &filename)
QIODevice::size
virtual qint64 size() const
KPixmapCache::cacheLimit
int cacheLimit() const
Definition: kpixmapcache.cpp:1122
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.h
QImage::width
int width() const
QList::isEmpty
bool isEmpty() const
QPainter
KPixmapCache::removeEntryStrategy
RemoveStrategy removeEntryStrategy() const
Definition: kpixmapcache.cpp:1146
QString::isEmpty
bool isEmpty() const
KPixmapCache::Private
friend class Private
Definition: kpixmapcache.h:379
QByteArray::constData
const char * constData() const
QPixmapCache::clear
void clear()
QIODevice::read
qint64 read(char *data, qint64 maxSize)
QString
QList< KPixmapCache::Private * >
QFileInfo::lastModified
QDateTime lastModified() const
KPixmapCache::deleteCache
static void deleteCache(const QString &name)
Deletes a pixmap cache.
Definition: kpixmapcache.cpp:1217
QFile::open
virtual bool open(QFlags< QIODevice::OpenModeFlag > mode)
QDataStream::atEnd
bool atEnd() const
KPixmapCache::setTimestamp
void setTimestamp(unsigned int time)
Sets the timestamp of app-specific cache.
Definition: kpixmapcache.cpp:1072
QPixmap
QFileInfo
KPixmapCache::~KPixmapCache
virtual ~KPixmapCache()
Definition: kpixmapcache.cpp:996
QList::end
iterator end()
QFileInfo::size
qint64 size() const
QFileInfo::exists
bool exists() const
QPixmap::isNull
bool isNull() const
QSize
QFile::size
virtual qint64 size() const
KSaveFile::finalize
bool finalize()
QLatin1Char
QFile::close
virtual void close()
KPIXMAPCACHE_VERSION
#define KPIXMAPCACHE_VERSION
Definition: kpixmapcache.cpp:67
QImage::numBytes
int numBytes() const
QImage
QDateTime::toTime_t
uint toTime_t() const
KLockFile::LockOK
KPixmapCache::removeEntries
void removeEntries(int newsize=0)
Removes some of the entries in the cache according to current removeEntryStrategy().
Definition: kpixmapcache.cpp:1258
QImage::bytesPerLine
int bytesPerLine() const
KPixmapCache::setRemoveEntryStrategy
void setRemoveEntryStrategy(RemoveStrategy strategy)
Sets the removeEntryStrategy used when removing entries.
Definition: kpixmapcache.cpp:1151
KPixmapCache::discard
void discard()
Deletes all entries and reinitializes this cache.
Definition: kpixmapcache.cpp:1226
QString::toLatin1
QByteArray toLatin1() const
KPixmapCache::writeCustomData
virtual bool writeCustomData(QDataStream &stream)
Can be used by subclasses to write custom data into the stream.
Definition: kpixmapcache.cpp:1429
KStandardDirs::locateLocal
static QString locateLocal(const char *type, const QString &filename, const KComponentData &cData=KGlobal::mainComponent())
QMutexLocker
QRectF
kstandarddirs.h
QIODevice::writeData
virtual qint64 writeData(const char *data, qint64 maxSize)=0
QPixmapCache::find
QPixmap * find(const QString &key)
QSize::height
int height() const
KPixmapCache::size
int size() const
Definition: kpixmapcache.cpp:1103
QByteArray::data
char * data()
kWarning
static QDebug kWarning(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
madvise
int madvise(caddr_t addr, size_t len, int advice)
qint32
QImage::bits
uchar * bits()
QIODevice::write
qint64 write(const char *data, qint64 maxSize)
QImage::height
int height() const
KLockFile::NoBlockFlag
ksvgrenderer.h
QPixmap::toImage
QImage toImage() const
KPixmapCache::setCacheLimit
void setCacheLimit(int kbytes)
Sets the maximum size of the cache (in kilobytes).
Definition: kpixmapcache.cpp:1127
QDataStream::device
QIODevice * device() const
QPixmapCache::insert
bool insert(const QString &key, const QPixmap &pixmap)
QByteArray::size
int size() const
QString::arg
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QImage::format
Format format() const
KPixmapCache::isEnabled
bool isEnabled() const
Cache will be disabled when e.g.
Definition: kpixmapcache.cpp:1048
QList::begin
iterator begin()
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::RemoveStrategy
RemoveStrategy
Describes which entries will be removed first during cache cleanup.
Definition: kpixmapcache.h:216
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:24:00 by doxygen 1.8.7 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 API Reference

Skip menu "kdelibs 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

Search



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