You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
digikam/digikam/libs/threadimageio/loadingcache.cpp

257 lines
6.4 KiB

/* ============================================================
*
* This file is a part of digiKam project
* http://www.digikam.org
*
* Date : 2006-01-11
* Description : shared image loading and caching
*
* Copyright (C) 2005-2007 by Marcel Wiesweg <marcel.wiesweg@gmx.de>
*
* This program is free software; you can redistribute it
* and/or modify it under the terms of the GNU General
* Public License as published by the Free Software Foundation;
* either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* ============================================================ */
// TQt includes.
#include <tqapplication.h>
#include <tqvariant.h>
// KDE includes.
#include <kdirwatch.h>
// Local includes.
#include "ddebug.h"
#include "loadingcache.h"
#include "loadingcache.moc"
namespace Digikam
{
class LoadingCachePriv
{
public:
TQCache<DImg> imageCache;
TQDict<LoadingProcess> loadingDict;
TQMutex mutex;
TQWaitCondition condVar;
KDirWatch *watch;
TQStringList watchedFiles;
};
LoadingCache *LoadingCache::m_instance = 0;
LoadingCache *LoadingCache::cache()
{
if (!m_instance)
m_instance = new LoadingCache;
return m_instance;
}
void LoadingCache::cleanUp()
{
if (m_instance)
delete m_instance;
}
LoadingCache::LoadingCache()
{
d = new LoadingCachePriv;
d->imageCache.setAutoDelete(true);
// default value: 60 MB of cache
setCacheSize(60);
d->watch = new KDirWatch;
connect(d->watch, TQT_SIGNAL(dirty(const TQString &)),
this, TQT_SLOT(slotFileDirty(const TQString &)));
}
LoadingCache::~LoadingCache()
{
delete d->watch;
delete d;
m_instance = 0;
}
DImg *LoadingCache::retrieveImage(const TQString &cacheKey)
{
return d->imageCache.find(cacheKey);
}
bool LoadingCache::putImage(const TQString &cacheKey, DImg *img, const TQString &filePath)
{
bool successfulyInserted;
// use size of image as cache cost, take care for wrapped preview TQImages
int cost = img->numBytes();
TQVariant attribute(img->attribute("previewTQImage"));
if (attribute.isValid())
{
cost = attribute.toImage().numBytes();
}
if ( d->imageCache.insert(cacheKey, img, cost) )
{
if (!filePath.isEmpty())
{
// store file path as attribute for our own use
img->setAttribute("loadingCacheFilePath", TQVariant(filePath));
}
successfulyInserted = true;
}
else
{
// need to delete object if it was not successfuly inserted (too large)
delete img;
successfulyInserted = false;
}
if (!filePath.isEmpty())
{
// schedule update of file watch
// KDirWatch can only be accessed from main thread!
TQApplication::postEvent(this, new TQCustomEvent(TQEvent::User));
}
return successfulyInserted;
}
void LoadingCache::removeImage(const TQString &cacheKey)
{
d->imageCache.remove(cacheKey);
}
void LoadingCache::removeImages()
{
d->imageCache.clear();
}
void LoadingCache::slotFileDirty(const TQString &path)
{
// Signal comes from main thread, we need to lock ourselves.
CacheLock lock(this);
//DDebug() << "LoadingCache slotFileDirty " << path << endl;
for (TQCacheIterator<DImg> it(d->imageCache); it.current(); ++it)
{
if (it.current()->attribute("loadingCacheFilePath").toString() == path)
{
//DDebug() << " removing watch and cache entry for " << path << endl;
d->imageCache.remove(it.currentKey());
d->watch->removeFile(path);
d->watchedFiles.remove(path);
}
}
}
void LoadingCache::customEvent(TQCustomEvent *)
{
// Event comes from main thread, we need to lock ourselves.
CacheLock lock(this);
// get a list of files in cache that need watch
TQStringList toBeAdded;
TQStringList toBeRemoved = d->watchedFiles;
for (TQCacheIterator<DImg> it(d->imageCache); it.current(); ++it)
{
TQString watchPath = it.current()->attribute("loadingCacheFilePath").toString();
if (!watchPath.isEmpty())
{
if (!d->watchedFiles.contains(watchPath))
toBeAdded.append(watchPath);
toBeRemoved.remove(watchPath);
}
}
for (TQStringList::iterator it = toBeRemoved.begin(); it != toBeRemoved.end(); ++it)
{
//DDebug() << "removing watch for " << *it << endl;
d->watch->removeFile(*it);
d->watchedFiles.remove(*it);
}
for (TQStringList::iterator it = toBeAdded.begin(); it != toBeAdded.end(); ++it)
{
//DDebug() << "adding watch for " << *it << endl;
d->watch->addFile(*it);
d->watchedFiles.append(*it);
}
}
bool LoadingCache::isCacheable(const DImg *img)
{
// return whether image fits in cache
return (uint)d->imageCache.maxCost() >= img->numBytes();
}
void LoadingCache::addLoadingProcess(LoadingProcess *process)
{
d->loadingDict.insert(process->cacheKey(), process);
}
LoadingProcess *LoadingCache::retrieveLoadingProcess(const TQString &cacheKey)
{
return d->loadingDict.find(cacheKey);
}
void LoadingCache::removeLoadingProcess(LoadingProcess *process)
{
d->loadingDict.remove(process->cacheKey());
}
void LoadingCache::notifyNewLoadingProcess(LoadingProcess *process, LoadingDescription description)
{
for (TQDictIterator<LoadingProcess> it(d->loadingDict); it.current(); ++it)
{
it.current()->notifyNewLoadingProcess(process, description);
}
}
void LoadingCache::setCacheSize(int megabytes)
{
d->imageCache.setMaxCost(megabytes * 1024 * 1024);
}
//---------------------------------------------------------------------------------------------------
LoadingCache::CacheLock::CacheLock(LoadingCache *cache)
: m_cache(cache)
{
m_cache->d->mutex.lock();
}
LoadingCache::CacheLock::~CacheLock()
{
m_cache->d->mutex.unlock();
}
void LoadingCache::CacheLock::wakeAll()
{
// obviously the mutex is locked when this function is called
m_cache->d->condVar.wakeAll();
}
void LoadingCache::CacheLock::timedWait()
{
// same as above, the mutex is certainly locked
m_cache->d->condVar.wait(&m_cache->d->mutex, 1000);
}
} // namespace Digikam