summaryrefslogtreecommitdiffstats
path: root/src/libs/threadimageio
diff options
context:
space:
mode:
authorMichele Calgaro <michele.calgaro@yahoo.it>2024-11-22 18:41:30 +0900
committerMichele Calgaro <michele.calgaro@yahoo.it>2024-11-22 18:41:30 +0900
commitee0d99607c14cb63d3ebdb3a970b508949fa8219 (patch)
tree94ac1efedb94cb38bf6879ba0610fe75b554216b /src/libs/threadimageio
parent4adff739380e4ae9f30e443ee95644f184456869 (diff)
downloaddigikam-ee0d99607c14cb63d3ebdb3a970b508949fa8219.tar.gz
digikam-ee0d99607c14cb63d3ebdb3a970b508949fa8219.zip
Rename 'digikam' folder to 'src'
Signed-off-by: Michele Calgaro <michele.calgaro@yahoo.it>
Diffstat (limited to 'src/libs/threadimageio')
-rw-r--r--src/libs/threadimageio/Makefile.am23
-rw-r--r--src/libs/threadimageio/loadingcache.cpp256
-rw-r--r--src/libs/threadimageio/loadingcache.h135
-rw-r--r--src/libs/threadimageio/loadingcacheinterface.cpp73
-rw-r--r--src/libs/threadimageio/loadingcacheinterface.h56
-rw-r--r--src/libs/threadimageio/loadingdescription.cpp152
-rw-r--r--src/libs/threadimageio/loadingdescription.h135
-rw-r--r--src/libs/threadimageio/loadsavetask.cpp424
-rw-r--r--src/libs/threadimageio/loadsavetask.h360
-rw-r--r--src/libs/threadimageio/loadsavethread.cpp331
-rw-r--r--src/libs/threadimageio/loadsavethread.h184
-rw-r--r--src/libs/threadimageio/managedloadsavethread.cpp362
-rw-r--r--src/libs/threadimageio/managedloadsavethread.h134
-rw-r--r--src/libs/threadimageio/previewloadthread.cpp52
-rw-r--r--src/libs/threadimageio/previewloadthread.h57
-rw-r--r--src/libs/threadimageio/previewtask.cpp258
-rw-r--r--src/libs/threadimageio/previewtask.h57
-rw-r--r--src/libs/threadimageio/sharedloadsavethread.cpp65
-rw-r--r--src/libs/threadimageio/sharedloadsavethread.h43
19 files changed, 3157 insertions, 0 deletions
diff --git a/src/libs/threadimageio/Makefile.am b/src/libs/threadimageio/Makefile.am
new file mode 100644
index 00000000..a301012e
--- /dev/null
+++ b/src/libs/threadimageio/Makefile.am
@@ -0,0 +1,23 @@
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libthreadimageio.la
+
+libthreadimageio_la_SOURCES = loadsavethread.cpp \
+ managedloadsavethread.cpp \
+ sharedloadsavethread.cpp \
+ previewloadthread.cpp \
+ loadingdescription.cpp \
+ loadsavetask.cpp \
+ previewtask.cpp \
+ loadingcache.cpp \
+ loadingcacheinterface.cpp
+
+libthreadimageio_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TQT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor
+
+INCLUDES = -I$(top_srcdir)/src/libs/dimg \
+ -I$(top_srcdir)/src/libs/dimg/loaders \
+ -I$(top_srcdir)/src/libs/dmetadata \
+ $(LIBKEXIV2_CFLAGS) \
+ -I$(top_srcdir)/src/libs/jpegutils \
+ -I$(top_srcdir)/src/digikam \
+ $(LIBKDCRAW_CFLAGS) $(all_includes)
diff --git a/src/libs/threadimageio/loadingcache.cpp b/src/libs/threadimageio/loadingcache.cpp
new file mode 100644
index 00000000..04dd3e4f
--- /dev/null
+++ b/src/libs/threadimageio/loadingcache.cpp
@@ -0,0 +1,256 @@
+/* ============================================================
+ *
+ * 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, TQ_SIGNAL(dirty(const TQString &)),
+ this, TQ_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
+
diff --git a/src/libs/threadimageio/loadingcache.h b/src/libs/threadimageio/loadingcache.h
new file mode 100644
index 00000000..d4689305
--- /dev/null
+++ b/src/libs/threadimageio/loadingcache.h
@@ -0,0 +1,135 @@
+/* ============================================================
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+#ifndef LOADING_CACHE_H
+#define LOADING_CACHE_H
+
+#include <tqptrlist.h>
+#include <tqcache.h>
+#include <tqdict.h>
+#include <tqmutex.h>
+
+#include "dimg.h"
+#include "loadsavethread.h"
+
+namespace Digikam
+{
+
+class LoadingProcessListener
+{
+public:
+
+ virtual bool querySendNotifyEvent() = 0;
+ virtual TQObject *eventReceiver() = 0;
+ virtual LoadSaveThread::AccessMode accessMode() = 0;
+
+};
+
+class LoadingProcess
+{
+public:
+
+ virtual bool completed() = 0;
+ virtual TQString filePath() = 0;
+ virtual TQString cacheKey() = 0;
+ virtual void addListener(LoadingProcessListener *listener) = 0;
+ virtual void removeListener(LoadingProcessListener *listener) = 0;
+ virtual void notifyNewLoadingProcess(LoadingProcess *process, LoadingDescription description) = 0;
+
+};
+
+class LoadingCachePriv;
+
+class LoadingCache : public TQObject
+{
+
+ TQ_OBJECT
+
+
+public:
+
+ static LoadingCache *cache();
+ static void cleanUp();
+ ~LoadingCache();
+
+ // all functions shall only be called when a CacheLock is held
+ class CacheLock
+ {
+ public:
+ CacheLock(LoadingCache *cache);
+ ~CacheLock();
+ void wakeAll();
+ void timedWait();
+ private:
+ LoadingCache *m_cache;
+ };
+
+ // Retrieves an image for the given string from the cache,
+ // or 0 if no image is found.
+ DImg *retrieveImage(const TQString &cacheKey);
+ // Returns whether the given DImg fits in the cache.
+ bool isCacheable(const DImg *img);
+ // Put image into for given string into the cache.
+ // Returns true if image has been put in the cache, false otherwise.
+ // Ownership of the DImg instance is passed to the cache.
+ // When it cannot be put in the cache it is deleted.
+ // The third parameter specifies a file path that will be watched.
+ // If this file changes, the object will be removed from the cache.
+ bool putImage(const TQString &cacheKey, DImg *img, const TQString &filePath);
+ void removeImage(const TQString &cacheKey);
+ void removeImages();
+
+ // Find the loading process for given cacheKey, or 0 if not found
+ LoadingProcess *retrieveLoadingProcess(const TQString &cacheKey);
+ // Add a loading process to the list. Only one loading process
+ // for the same cache key is registered at a time.
+ void addLoadingProcess(LoadingProcess *process);
+ // Remove loading process for given cache key
+ void removeLoadingProcess(LoadingProcess *process);
+ // Notify all currently registered loading processes
+ void notifyNewLoadingProcess(LoadingProcess *process, LoadingDescription description);
+
+ void setCacheSize(int megabytes);
+
+protected:
+
+ virtual void customEvent (TQCustomEvent *event);
+
+private slots:
+
+ void slotFileDirty(const TQString &path);
+
+private:
+
+ static LoadingCache *m_instance;
+
+ LoadingCache();
+
+ friend class CacheLock;
+ LoadingCachePriv *d;
+
+};
+
+} // namespace Digikam
+
+#endif
diff --git a/src/libs/threadimageio/loadingcacheinterface.cpp b/src/libs/threadimageio/loadingcacheinterface.cpp
new file mode 100644
index 00000000..8cfa5ab9
--- /dev/null
+++ b/src/libs/threadimageio/loadingcacheinterface.cpp
@@ -0,0 +1,73 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2006-02-06
+ * 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.
+ *
+ * ============================================================ */
+
+
+#include "loadingcacheinterface.h"
+#include "loadingcache.h"
+
+namespace Digikam
+{
+
+void LoadingCacheInterface::cleanUp()
+{
+ LoadingCache::cleanUp();
+}
+
+void LoadingCacheInterface::cleanFromCache(const TQString &filePath)
+{
+ LoadingCache *cache = LoadingCache::cache();
+ LoadingCache::CacheLock lock(cache);
+ TQStringList possibleCacheKeys = LoadingDescription::possibleCacheKeys(filePath);
+ for (TQStringList::iterator it = possibleCacheKeys.begin(); it != possibleCacheKeys.end(); ++it)
+ {
+ cache->removeImage(*it);
+ }
+}
+
+void LoadingCacheInterface::cleanCache()
+{
+ LoadingCache *cache = LoadingCache::cache();
+ LoadingCache::CacheLock lock(cache);
+ cache->removeImages();
+}
+
+void LoadingCacheInterface::putImage(const TQString &filePath, const DImg &img)
+{
+ LoadingCache *cache = LoadingCache::cache();
+ LoadingCache::CacheLock lock(cache);
+ if (cache->isCacheable(&img))
+ {
+ DImg *copy = new DImg(img);
+ copy->detach();
+ cache->putImage(filePath, copy, filePath);
+ }
+}
+
+void LoadingCacheInterface::setCacheOptions(int cacheSize)
+{
+ LoadingCache *cache = LoadingCache::cache();
+ LoadingCache::CacheLock lock(cache);
+ cache->setCacheSize(cacheSize);
+}
+
+} // namespace Digikam
diff --git a/src/libs/threadimageio/loadingcacheinterface.h b/src/libs/threadimageio/loadingcacheinterface.h
new file mode 100644
index 00000000..ab3f4eca
--- /dev/null
+++ b/src/libs/threadimageio/loadingcacheinterface.h
@@ -0,0 +1,56 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2006-02-06
+ * 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.
+ *
+ * ============================================================ */
+
+#ifndef LOADING_CACHE_INTERFACE_H
+#define LOADING_CACHE_INTERFACE_H
+
+#include <tqstring.h>
+
+#include "digikam_export.h"
+#include "dimg.h"
+
+namespace Digikam
+{
+
+class DIGIKAM_EXPORT LoadingCacheInterface
+{
+public:
+ // clean up cache at shutdown
+ static void cleanUp();
+ // remove an image from the cache
+ // (e.g. when image has changed on disk)
+ static void cleanFromCache(const TQString &filePath);
+ // remove all images from the cache
+ // (e.g. when loading settings changed)
+ static void cleanCache();
+ // add a copy of the image to cache
+ static void putImage(const TQString &filePath, const DImg &img);
+ // Set cache size in Megabytes.
+ // Set to 0 to disable caching.
+ static void setCacheOptions(int cacheSize);
+};
+
+} // namespace Digikam
+
+#endif
+
diff --git a/src/libs/threadimageio/loadingdescription.cpp b/src/libs/threadimageio/loadingdescription.cpp
new file mode 100644
index 00000000..423ee99c
--- /dev/null
+++ b/src/libs/threadimageio/loadingdescription.cpp
@@ -0,0 +1,152 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2007-02-03
+ * Description : Loading parameters for multithreaded loading
+ *
+ * Copyright (C) 2006-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.
+ *
+ * ============================================================ */
+
+// Local includes.
+
+#include "loadingdescription.h"
+
+namespace Digikam
+{
+
+bool LoadingDescription::PreviewParameters::operator==(const PreviewParameters &other) const
+{
+ return isPreview == other.isPreview
+ && size == other.size
+ && exifRotate == other.exifRotate;
+}
+
+LoadingDescription::LoadingDescription(const TQString &filePath)
+ : filePath(filePath)
+{
+ rawDecodingSettings = DRawDecoding();
+}
+
+LoadingDescription::LoadingDescription(const TQString &filePath, DRawDecoding settings)
+ : filePath(filePath), rawDecodingSettings(settings)
+{
+}
+
+LoadingDescription::LoadingDescription(const TQString &filePath, int size, bool exifRotate)
+ : filePath(filePath)
+{
+ rawDecodingSettings = DRawDecoding();
+ previewParameters.isPreview = false;
+ previewParameters.size = size;
+ previewParameters.exifRotate = exifRotate;
+}
+
+TQString LoadingDescription::cacheKey() const
+{
+ // Here we have the knowledge which LoadingDescriptions / RawFileDecodingSettings
+ // must be cached separately.
+ // Current assumption:
+ // Eight-bit images are needed for LightTable, and if 16-bit is enabled,
+ // 16-bit half size images for the histogram sidebar,
+ // and 16-bit full size images for the image editor.
+ // Use previewParameters.size, not isPreview - if it is 0, full loading is used.
+
+ TQString suffix = rawDecodingSettings.sixteenBitsImage ? "-16" : "-8";
+
+ if (rawDecodingSettings.halfSizeColorImage)
+ return filePath + suffix + "-halfSizeColorImage";
+ else if (previewParameters.size)
+ return filePath + suffix + "-previewImage";
+ else
+ return filePath + suffix;
+}
+
+TQStringList LoadingDescription::lookupCacheKeys() const
+{
+ // Build a hierarchy which cache entries may be used for this LoadingDescription.
+ // Typically, the first is the best, but an actual loading operation may use a
+ // lower-quality loading and will effectively only add the last entry of the
+ // list to the cache, although it can accept the first if already available.
+ // Sixteen-bit images cannot be used used instead of eight-bit ones because
+ // color management is needed to display them.
+
+ TQString suffix = rawDecodingSettings.sixteenBitsImage ? "-16" : "-8";
+
+ TQStringList keys;
+ keys.append(filePath + suffix);
+ if (rawDecodingSettings.halfSizeColorImage)
+ keys.append(filePath + suffix + "-halfSizeColorImage");
+ if (previewParameters.size)
+ keys.append(filePath + suffix + "-previewImage");
+ return keys;
+}
+
+bool LoadingDescription::isReducedVersion() const
+{
+ // return true if this loads anything but the full version
+ return rawDecodingSettings.halfSizeColorImage
+ || previewParameters.isPreview;
+}
+
+bool LoadingDescription::operator==(const LoadingDescription &other) const
+{
+ return filePath == other.filePath &&
+ rawDecodingSettings == other.rawDecodingSettings &&
+ previewParameters == other.previewParameters;
+}
+
+bool LoadingDescription::equalsIgnoreReducedVersion(const LoadingDescription &other) const
+{
+ return filePath == other.filePath;
+}
+
+bool LoadingDescription::equalsOrBetterThan(const LoadingDescription &other) const
+{
+ // This method is similar to operator==. But it returns true as well if other
+ // Loads a "better" version than this.
+ // Preview parameters must have the same size, or other has no size restriction.
+ // All raw decoding settings must be equal, only the half size parameter is allowed to vary.
+
+ DRawDecoding fullSize = other.rawDecodingSettings;
+ fullSize.halfSizeColorImage = false;
+
+ return filePath == other.filePath &&
+ (
+ rawDecodingSettings == other.rawDecodingSettings ||
+ rawDecodingSettings == fullSize
+ ) &&
+ (
+ (previewParameters.size == other.previewParameters.size) ||
+ other.previewParameters.size
+ );
+}
+
+TQStringList LoadingDescription::possibleCacheKeys(const TQString &filePath)
+{
+ TQStringList keys;
+ keys.append(filePath + "-16");
+ keys.append(filePath + "-16-halfSizeColorImage");
+ keys.append(filePath + "-16-previewImage");
+ keys.append(filePath + "-8");
+ keys.append(filePath + "-8-halfSizeColorImage");
+ keys.append(filePath + "-8-previewImage");
+ return keys;
+}
+
+
+} // namespace Digikam
+
diff --git a/src/libs/threadimageio/loadingdescription.h b/src/libs/threadimageio/loadingdescription.h
new file mode 100644
index 00000000..848f2b17
--- /dev/null
+++ b/src/libs/threadimageio/loadingdescription.h
@@ -0,0 +1,135 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2006-01-16
+ * Description : image file IO threaded interface.
+ *
+ * Copyright (C) 2006-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.
+ *
+ * ============================================================ */
+
+#ifndef LOADING_DESCRIPTION_H
+#define LOADING_DESCRIPTION_H
+
+// Digikam includes.
+
+#include "dimg.h"
+#include "digikam_export.h"
+
+namespace Digikam
+{
+
+class DIGIKAM_EXPORT LoadingDescription
+{
+public:
+
+ class PreviewParameters
+ {
+ public:
+ PreviewParameters()
+ {
+ isPreview = false;
+ size = 0;
+ exifRotate = false;
+ }
+
+ bool isPreview;
+ int size;
+ bool exifRotate;
+
+ bool operator==(const PreviewParameters &other) const;
+ };
+
+ /**
+ * An invalid LoadingDescription
+ */
+ LoadingDescription()
+ {
+ }
+
+ /**
+ * Use this for files that are not raw files.
+ * Stores only the filePath.
+ */
+ LoadingDescription(const TQString &filePath);
+
+ /**
+ * For raw files:
+ * Stores filePath and RawDecodingSettings
+ */
+ LoadingDescription(const TQString &filePath, DRawDecoding settings);
+
+ /**
+ * For preview jobs:
+ * Stores preview max size and exif rotation.
+ * Exif Rotation:
+ * The exif rotation is only a hint.
+ * Call LoadSaveThread::exifRotate to make sure that the image is really
+ * rotated. It is safe to call this method even if the image is rotated.
+ * Raw files:
+ * If size is not 0, the embedded preview will be loaded if available.
+ * If size is 0, DImg based loading will be used with default raw decoding settings.
+ */
+ LoadingDescription(const TQString &filePath, int size, bool exifRotate);
+
+ TQString filePath;
+ DRawDecoding rawDecodingSettings;
+ PreviewParameters previewParameters;
+
+ /**
+ * Return the cache key this description shall be stored as
+ */
+ TQString cacheKey() const;
+ /**
+ * Return all possible cache keys, starting with the best choice,
+ * for which a result may be found in the cache for this description.
+ * Included in the list are better quality versions, if this description is reduced.
+ */
+ TQStringList lookupCacheKeys() const;
+ /**
+ * Returns whether this description describes a loading operation which
+ * loads the image in a reduced version (quality, size etc.)
+ */
+ bool isReducedVersion() const;
+
+ /**
+ * Returns whether the other loading task equals this one
+ */
+ bool operator==(const LoadingDescription &other) const;
+ bool operator!=(const LoadingDescription &other) const
+ { return !operator==(other); }
+ /**
+ * Returns whether the other loading task equals this one
+ * ignoring parameters used to specify a reduced version.
+ */
+ bool equalsIgnoreReducedVersion(const LoadingDescription &other) const;
+
+ /**
+ * Returns whether this loading task equals the other one
+ * or is superior to it, if the other one is a reduced version
+ */
+ bool equalsOrBetterThan(const LoadingDescription &other) const;
+
+ /**
+ * Returns all possible cacheKeys for the given file path
+ * (all cache keys under which the given file could be stored in the cache).
+ */
+ static TQStringList possibleCacheKeys(const TQString &filePath);
+};
+
+} // namespace Digikam
+
+#endif // LOADING_DESCRIPTION_H
diff --git a/src/libs/threadimageio/loadsavetask.cpp b/src/libs/threadimageio/loadsavetask.cpp
new file mode 100644
index 00000000..6879e533
--- /dev/null
+++ b/src/libs/threadimageio/loadsavetask.cpp
@@ -0,0 +1,424 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-12-17
+ * Description : image file IO threaded interface.
+ *
+ * Copyright (C) 2005-2007 by Marcel Wiesweg <marcel.wiesweg@gmx.de>
+ * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+#include "loadsavetask.h"
+
+// TQt includes.
+
+#include <tqapplication.h>
+
+// Local includes.
+
+#include "ddebug.h"
+#include "loadsavethread.h"
+#include "managedloadsavethread.h"
+#include "sharedloadsavethread.h"
+#include "loadingcache.h"
+
+namespace Digikam
+{
+
+void LoadingProgressEvent::notify(LoadSaveThread *thread)
+{
+ thread->loadingProgress(m_loadingDescription, m_progress);
+}
+
+void SavingProgressEvent::notify(LoadSaveThread *thread)
+{
+ thread->savingProgress(m_filePath, m_progress);
+}
+
+void StartedLoadingEvent::notify(LoadSaveThread *thread)
+{
+ thread->imageStartedLoading(m_loadingDescription);
+}
+
+void StartedSavingEvent::notify(LoadSaveThread *thread)
+{
+ thread->imageStartedSaving(m_filePath);
+}
+
+void LoadedEvent::notify(LoadSaveThread *thread)
+{
+ thread->imageLoaded(m_loadingDescription, m_img);
+}
+
+void MoreCompleteLoadingAvailableEvent::notify(LoadSaveThread *thread)
+{
+ thread->moreCompleteLoadingAvailable(m_oldDescription, m_newDescription);
+}
+
+void SavedEvent::notify(LoadSaveThread *thread)
+{
+ thread->imageSaved(m_filePath, m_success);
+}
+
+//---------------------------------------------------------------------------------------------------
+
+void LoadingTask::execute()
+{
+ if (m_loadingTaskStatus == LoadingTaskStatusStopping)
+ return;
+ DImg img(m_loadingDescription.filePath, this, m_loadingDescription.rawDecodingSettings);
+ m_thread->taskHasFinished();
+ TQApplication::postEvent(m_thread, new LoadedEvent(m_loadingDescription.filePath, img));
+}
+
+LoadingTask::TaskType LoadingTask::type()
+{
+ return TaskTypeLoading;
+}
+
+void LoadingTask::progressInfo(const DImg *, float progress)
+{
+ if (m_loadingTaskStatus == LoadingTaskStatusLoading)
+ {
+ if (m_thread->querySendNotifyEvent())
+ TQApplication::postEvent(m_thread, new LoadingProgressEvent(m_loadingDescription.filePath, progress));
+ }
+}
+
+bool LoadingTask::continueQuery(const DImg *)
+{
+ return m_loadingTaskStatus != LoadingTaskStatusStopping;
+}
+
+void LoadingTask::setStatus(LoadingTaskStatus status)
+{
+ m_loadingTaskStatus = status;
+}
+
+
+// This is a hack needed to prevent hanging when a TDEProcess-based loader (raw loader)
+// is waiting for the process to finish, but the main thread is waiting
+// for the thread to finish and no TDEProcess events are delivered.
+// Remove when porting to TQt4.
+bool LoadingTask::isShuttingDown()
+{
+ return m_thread->isShuttingDown();
+}
+
+//---------------------------------------------------------------------------------------------------
+
+void SharedLoadingTask::execute()
+{
+ if (m_loadingTaskStatus == LoadingTaskStatusStopping)
+ return;
+ // send StartedLoadingEvent from each single Task, not via LoadingProcess list
+ TQApplication::postEvent(m_thread, new StartedLoadingEvent(m_loadingDescription.filePath));
+
+ LoadingCache *cache = LoadingCache::cache();
+ {
+ LoadingCache::CacheLock lock(cache);
+
+ // find possible cached images
+ DImg *cachedImg = 0;
+ TQStringList lookupKeys = m_loadingDescription.lookupCacheKeys();
+ for ( TQStringList::Iterator it = lookupKeys.begin(); it != lookupKeys.end(); ++it ) {
+ if ( (cachedImg = cache->retrieveImage(*it)) )
+ break;
+ }
+
+ if (cachedImg)
+ {
+ // image is found in image cache, loading is successful
+ DImg img(*cachedImg);
+ if (accessMode() == LoadSaveThread::AccessModeReadWrite)
+ img = img.copy();
+ TQApplication::postEvent(m_thread, new LoadedEvent(m_loadingDescription.filePath, img));
+ return;
+ }
+ else
+ {
+ // find possible running loading process
+ m_usedProcess = 0;
+ for ( TQStringList::Iterator it = lookupKeys.begin(); it != lookupKeys.end(); ++it ) {
+ if ( (m_usedProcess = cache->retrieveLoadingProcess(*it)) )
+ {
+ break;
+ }
+ }
+
+ if (m_usedProcess)
+ {
+ // Other process is right now loading this image.
+ // Add this task to the list of listeners and
+ // attach this thread to the other thread, wait until loading
+ // has finished.
+ m_usedProcess->addListener(this);
+ // break loop when either the loading has completed, or this task is being stopped
+ while ( !m_usedProcess->completed() && m_loadingTaskStatus != LoadingTaskStatusStopping )
+ lock.timedWait();
+ // remove listener from process
+ m_usedProcess->removeListener(this);
+ // wake up the process which is waiting until all listeners have removed themselves
+ lock.wakeAll();
+ // set to 0, as checked in setStatus
+ m_usedProcess = 0;
+ //DDebug() << "SharedLoadingTask " << this << ": waited" << endl;
+ return;
+ }
+ else
+ {
+ // Neither in cache, nor currently loading in different thread.
+ // Load it here and now, add this LoadingProcess to cache list.
+ cache->addLoadingProcess(this);
+ // Add this to the list of listeners
+ addListener(this);
+ // for use in setStatus
+ m_usedProcess = this;
+ // Notify other processes that we are now loading this image.
+ // They might be interested - see notifyNewLoadingProcess below
+ cache->notifyNewLoadingProcess(this, m_loadingDescription);
+ }
+ }
+ }
+
+ // load image
+ DImg img(m_loadingDescription.filePath, this, m_loadingDescription.rawDecodingSettings);
+
+ bool isCached = false;
+ {
+ LoadingCache::CacheLock lock(cache);
+ // put (valid) image into cache of loaded images
+ if (!img.isNull())
+ isCached = cache->putImage(m_loadingDescription.cacheKey(), new DImg(img), m_loadingDescription.filePath);
+ // remove this from the list of loading processes in cache
+ cache->removeLoadingProcess(this);
+ }
+
+ // following the golden rule to avoid deadlocks, do this when CacheLock is not held
+ m_thread->taskHasFinished();
+
+ {
+ LoadingCache::CacheLock lock(cache);
+ //DDebug() << "SharedLoadingTask " << this << ": image loaded, " << img.isNull() << endl;
+ // indicate that loading has finished so that listeners can stop waiting
+ m_completed = true;
+
+ // Optimize so that no unnecessary copying is done.
+ // If image has been put in cache, the initial copy has been consumed for this.
+ // If image is too large for cache, the initial copy is still available.
+ bool usedInitialCopy = isCached;
+ // dispatch image to all listeners, including this
+ for (LoadingProcessListener *l = m_listeners.first(); l; l = m_listeners.next())
+ {
+ // This code sends a copy only when ReadWrite access is requested.
+ // Otherwise, the image from the cache is sent.
+ // As the image in the cache will be deleted from any thread, the explicit sharing
+ // needs to be thread-safe to avoid the risk of memory leaks.
+ // This is the case only for TQt4, so uncomment this code when porting.
+ /*
+ if (l->accessMode() == LoadSaveThread::AccessModeReadWrite)
+ {
+ // If a listener requested ReadWrite access, it gets a deep copy.
+ // DImg is explicitly shared.
+ DImg copy = img.copy();
+ TQApplication::postEvent(l->eventReceiver(), new LoadedEvent(m_loadingDescription.filePath, copy));
+ }
+ else
+ TQApplication::postEvent(l->eventReceiver(), new LoadedEvent(m_loadingDescription.filePath, img));
+ */
+ // TQt3: The same copy for all Read listeners (it is assumed that they will delete it only in the main thread),
+ // an extra copy for each ReadWrite listener
+ DImg readerCopy;
+ if (l->accessMode() == LoadSaveThread::AccessModeReadWrite)
+ {
+ // If a listener requested ReadWrite access, it gets a deep copy.
+ // DImg is explicitly shared.
+ DImg copy;
+ if (usedInitialCopy)
+ {
+ copy = img.copy();
+ }
+ else
+ {
+ copy = img;
+ usedInitialCopy = true;
+ }
+ TQApplication::postEvent(l->eventReceiver(), new LoadedEvent(m_loadingDescription, copy));
+ }
+ else
+ {
+ if (readerCopy.isNull())
+ {
+ if (usedInitialCopy)
+ {
+ readerCopy = img.copy();
+ }
+ else
+ {
+ readerCopy = img;
+ usedInitialCopy = true;
+ }
+ }
+ TQApplication::postEvent(l->eventReceiver(), new LoadedEvent(m_loadingDescription, readerCopy));
+ }
+ }
+
+ // remove myself from list of listeners
+ removeListener(this);
+ // wake all listeners waiting on cache condVar, so that they remove themselves
+ lock.wakeAll();
+ // wait until all listeners have removed themselves
+ while (m_listeners.count() != 0)
+ lock.timedWait();
+ // set to 0, as checked in setStatus
+ m_usedProcess = 0;
+ }
+}
+
+void SharedLoadingTask::progressInfo(const DImg *, float progress)
+{
+ if (m_loadingTaskStatus == LoadingTaskStatusLoading)
+ {
+ LoadingCache *cache = LoadingCache::cache();
+ LoadingCache::CacheLock lock(cache);
+
+ for (LoadingProcessListener *l = m_listeners.first(); l; l = m_listeners.next())
+ {
+ if (l->querySendNotifyEvent())
+ TQApplication::postEvent(l->eventReceiver(), new LoadingProgressEvent(m_loadingDescription, progress));
+ }
+ }
+}
+
+bool SharedLoadingTask::continueQuery(const DImg *)
+{
+ // If this is called, the thread is currently loading an image.
+ // In shared loading, we cannot stop until all listeners have been removed as well
+ return (m_loadingTaskStatus != LoadingTaskStatusStopping) || (m_listeners.count() != 0);
+}
+
+void SharedLoadingTask::setStatus(LoadingTaskStatus status)
+{
+ m_loadingTaskStatus = status;
+ if (m_loadingTaskStatus == LoadingTaskStatusStopping)
+ {
+ LoadingCache *cache = LoadingCache::cache();
+ LoadingCache::CacheLock lock(cache);
+
+ // check for m_usedProcess, to avoid race condition that it has finished before
+ if (m_usedProcess)
+ {
+ // remove this from list of listeners - check in continueQuery() of active thread
+ m_usedProcess->removeListener(this);
+ // wake all listeners - particularly this - from waiting on cache condvar
+ lock.wakeAll();
+ }
+ }
+}
+
+bool SharedLoadingTask::completed()
+{
+ return m_completed;
+}
+
+TQString SharedLoadingTask::filePath()
+{
+ return m_loadingDescription.filePath;
+}
+
+TQString SharedLoadingTask::cacheKey()
+{
+ return m_loadingDescription.cacheKey();
+}
+
+void SharedLoadingTask::addListener(LoadingProcessListener *listener)
+{
+ m_listeners.append(listener);
+}
+
+void SharedLoadingTask::removeListener(LoadingProcessListener *listener)
+{
+ m_listeners.remove(listener);
+}
+
+void SharedLoadingTask::notifyNewLoadingProcess(LoadingProcess *process, LoadingDescription description)
+{
+ // Ok, we are notified that another task has been started in another thread.
+ // We are of course only interested if the task loads the same file,
+ // and we are right now loading a reduced version, and the other task is loading the full version.
+ // In this case, we notify our own thread (a signal to the API user is emitted) of this.
+ // The fact that we are receiving the method call shows that this task is registered with the LoadingCache,
+ // somewhere in between the calls to addLoadingProcess(this) and removeLoadingProcess(this) above.
+ if (process != this &&
+ m_loadingDescription.isReducedVersion() &&
+ m_loadingDescription.equalsIgnoreReducedVersion(description) &&
+ !description.isReducedVersion()
+ )
+ {
+ for (LoadingProcessListener *l = m_listeners.first(); l; l = m_listeners.next())
+ {
+ TQApplication::postEvent(l->eventReceiver(), new MoreCompleteLoadingAvailableEvent(m_loadingDescription, description));
+ }
+ }
+}
+
+bool SharedLoadingTask::querySendNotifyEvent()
+{
+ return m_thread->querySendNotifyEvent();
+}
+
+TQObject *SharedLoadingTask::eventReceiver()
+{
+ return m_thread;
+}
+
+LoadSaveThread::AccessMode SharedLoadingTask::accessMode()
+{
+ return m_accessMode;
+}
+
+//---------------------------------------------------------------------------------------------------
+
+void SavingTask::execute()
+{
+ bool success = m_img.save(m_filePath, m_format, this);
+ m_thread->taskHasFinished();
+ TQApplication::postEvent(m_thread, new SavedEvent(m_filePath, success));
+};
+
+LoadingTask::TaskType SavingTask::type()
+{
+ return TaskTypeSaving;
+}
+
+void SavingTask::progressInfo(const DImg *, float progress)
+{
+ if (m_thread->querySendNotifyEvent())
+ TQApplication::postEvent(m_thread, new SavingProgressEvent(m_filePath, progress));
+}
+
+bool SavingTask::continueQuery(const DImg *)
+{
+ return m_savingTaskStatus != SavingTaskStatusStopping;
+}
+
+void SavingTask::setStatus(SavingTaskStatus status)
+{
+ m_savingTaskStatus = status;
+}
+
+} //namespace Digikam
diff --git a/src/libs/threadimageio/loadsavetask.h b/src/libs/threadimageio/loadsavetask.h
new file mode 100644
index 00000000..833f86fa
--- /dev/null
+++ b/src/libs/threadimageio/loadsavetask.h
@@ -0,0 +1,360 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2006-01-20
+ * Description : image file IO threaded interface.
+ *
+ * Copyright (C) 2005-2007 by Marcel Wiesweg <marcel.wiesweg@gmx.de>
+ * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+
+#ifndef LOAD_SAVE_TASK_H
+#define LOAD_SAVE_TASK_H
+
+// TQt includes.
+
+#include <tqevent.h>
+
+// Local includes.
+
+#include "dimg.h"
+#include "dimgloaderobserver.h"
+#include "loadingdescription.h"
+#include "loadingcache.h"
+
+namespace Digikam
+{
+
+class LoadSaveThread;
+
+class LoadSaveTask
+{
+public:
+
+ LoadSaveTask(LoadSaveThread* thread)
+ : m_thread(thread)
+ {};
+ virtual ~LoadSaveTask() {};
+
+ virtual void execute() = 0;
+
+ enum TaskType
+ {
+ TaskTypeLoading,
+ TaskTypeSaving
+ };
+
+ virtual TaskType type() = 0;
+
+protected:
+
+ LoadSaveThread *m_thread;
+};
+
+//---------------------------------------------------------------------------------------------------
+
+class NotifyEvent : public TQCustomEvent
+{
+public:
+
+ static TQEvent::Type notifyEventId()
+ { return TQEvent::User; };
+
+ NotifyEvent() : TQCustomEvent(notifyEventId()) {};
+
+ virtual void notify(LoadSaveThread *thread) = 0;
+};
+
+//---------------------------------------------------------------------------------------------------
+
+class ProgressEvent : public NotifyEvent
+{
+public:
+
+ ProgressEvent(float progress)
+ : m_progress(progress)
+ {};
+
+protected:
+
+ float m_progress;
+};
+
+//---------------------------------------------------------------------------------------------------
+
+class LoadingProgressEvent : public ProgressEvent
+{
+public:
+
+ LoadingProgressEvent(const LoadingDescription &loadingDescription, float progress)
+ : ProgressEvent(progress),
+ m_loadingDescription(loadingDescription)
+ {};
+
+ virtual void notify(LoadSaveThread *thread);
+
+private:
+
+ LoadingDescription m_loadingDescription;
+};
+
+//---------------------------------------------------------------------------------------------------
+
+class SavingProgressEvent : public ProgressEvent
+{
+public:
+
+ SavingProgressEvent(const TQString& filePath, float progress)
+ : ProgressEvent(progress),
+ m_filePath(filePath)
+ {};
+
+ virtual void notify(LoadSaveThread *thread);
+
+private:
+
+ TQString m_filePath;
+};
+
+//---------------------------------------------------------------------------------------------------
+
+class StartedLoadingEvent : public NotifyEvent
+{
+public:
+
+ StartedLoadingEvent(const LoadingDescription &loadingDescription)
+ : m_loadingDescription(loadingDescription)
+ {};
+
+ virtual void notify(LoadSaveThread *thread);
+
+private:
+
+ LoadingDescription m_loadingDescription;
+};
+
+//---------------------------------------------------------------------------------------------------
+
+class StartedSavingEvent : public NotifyEvent
+{
+public:
+
+ StartedSavingEvent(const TQString& filePath)
+ : m_filePath(filePath)
+ {};
+
+ virtual void notify(LoadSaveThread *thread);
+
+private:
+
+ TQString m_filePath;
+};
+
+//---------------------------------------------------------------------------------------------------
+
+class LoadedEvent : public NotifyEvent
+{
+public:
+
+ LoadedEvent(const LoadingDescription &loadingDescription, DImg &img)
+ : m_loadingDescription(loadingDescription), m_img(img)
+ {};
+
+ virtual void notify(LoadSaveThread *thread);
+
+private:
+
+ LoadingDescription m_loadingDescription;
+ DImg m_img;
+};
+
+//---------------------------------------------------------------------------------------------------
+
+class MoreCompleteLoadingAvailableEvent : public NotifyEvent
+{
+public:
+
+ MoreCompleteLoadingAvailableEvent(const LoadingDescription &oldLoadingDescription,
+ const LoadingDescription &newLoadingDescription)
+ : m_oldDescription(oldLoadingDescription), m_newDescription(newLoadingDescription)
+ {};
+
+ virtual void notify(LoadSaveThread *thread);
+
+private:
+
+ LoadingDescription m_oldDescription;
+ LoadingDescription m_newDescription;
+};
+
+//---------------------------------------------------------------------------------------------------
+
+class LoadingTask : public LoadSaveTask, public DImgLoaderObserver
+{
+public:
+
+ enum LoadingTaskStatus
+ {
+ LoadingTaskStatusLoading,
+ LoadingTaskStatusPreloading,
+ LoadingTaskStatusStopping
+ };
+
+ LoadingTask(LoadSaveThread* thread, LoadingDescription description,
+ LoadingTaskStatus loadingTaskStatus = LoadingTaskStatusLoading)
+ : LoadSaveTask(thread), m_loadingDescription(description), m_loadingTaskStatus(loadingTaskStatus)
+ {}
+
+ // LoadSaveTask
+
+ virtual void execute();
+ virtual TaskType type();
+
+ // DImgLoaderObserver
+
+ virtual void progressInfo(const DImg *, float progress);
+ virtual bool continueQuery(const DImg *);
+ virtual bool isShuttingDown();
+
+ virtual void setStatus(LoadingTaskStatus status);
+
+ LoadingTaskStatus status() const
+ {
+ return m_loadingTaskStatus;
+ }
+
+ TQString filePath() const
+ {
+ return m_loadingDescription.filePath;
+ }
+
+ LoadingDescription loadingDescription() const
+ {
+ return m_loadingDescription;
+ }
+
+protected:
+
+ LoadingDescription m_loadingDescription;
+ LoadingTaskStatus m_loadingTaskStatus;
+};
+
+//---------------------------------------------------------------------------------------------------
+
+class SharedLoadingTask : public LoadingTask, public LoadingProcess, public LoadingProcessListener
+{
+public:
+
+ SharedLoadingTask(LoadSaveThread* thread, LoadingDescription description,
+ LoadSaveThread::AccessMode mode = LoadSaveThread::AccessModeReadWrite,
+ LoadingTaskStatus loadingTaskStatus = LoadingTaskStatusLoading)
+ : LoadingTask(thread, description, loadingTaskStatus),
+ m_accessMode(mode), m_completed(false), m_usedProcess(0)
+ {}
+
+ virtual void execute();
+ virtual void progressInfo(const DImg *, float progress);
+ virtual bool continueQuery(const DImg *);
+ virtual void setStatus(LoadingTaskStatus status);
+
+ // LoadingProcess
+
+ virtual bool completed();
+ virtual TQString filePath();
+ virtual TQString cacheKey();
+ virtual void addListener(LoadingProcessListener *listener);
+ virtual void removeListener(LoadingProcessListener *listener);
+ virtual void notifyNewLoadingProcess(LoadingProcess *process, LoadingDescription description);
+
+ // LoadingProcessListener
+
+ virtual bool querySendNotifyEvent();
+ virtual TQObject *eventReceiver();
+ virtual LoadSaveThread::AccessMode accessMode();
+
+protected:
+
+ LoadSaveThread::AccessMode m_accessMode;
+ bool m_completed;
+ LoadingProcess *m_usedProcess;
+ TQPtrList<LoadingProcessListener> m_listeners;
+};
+
+//---------------------------------------------------------------------------------------------------
+
+class SavedEvent : public NotifyEvent
+{
+public:
+
+ SavedEvent(const TQString &filePath, bool success)
+ : m_filePath(filePath), m_success(success)
+ {};
+
+ virtual void notify(LoadSaveThread *thread);
+
+private:
+
+ TQString m_filePath;
+ bool m_success;
+};
+
+//---------------------------------------------------------------------------------------------------
+
+class SavingTask : public LoadSaveTask, public DImgLoaderObserver
+{
+public:
+
+ enum SavingTaskStatus
+ {
+ SavingTaskStatusSaving,
+ SavingTaskStatusStopping
+ };
+
+ SavingTask(LoadSaveThread* thread, DImg &img, const TQString &filePath, const TQString &format)
+ : LoadSaveTask(thread), m_img(img), m_filePath(filePath), m_format(format)
+ {};
+
+ virtual void execute();
+ virtual TaskType type();
+
+ virtual void progressInfo(const DImg *, float progress);
+ virtual bool continueQuery(const DImg *);
+
+ virtual void setStatus(SavingTaskStatus status);
+
+ SavingTaskStatus status() const
+ {
+ return m_savingTaskStatus;
+ }
+
+ TQString filePath() const
+ {
+ return m_filePath;
+ }
+
+private:
+
+ DImg m_img;
+ TQString m_filePath;
+ TQString m_format;
+ SavingTaskStatus m_savingTaskStatus;
+};
+
+} // namespace Digikam
+
+#endif // LOAD_SAVE_TASK_H
diff --git a/src/libs/threadimageio/loadsavethread.cpp b/src/libs/threadimageio/loadsavethread.cpp
new file mode 100644
index 00000000..188592e9
--- /dev/null
+++ b/src/libs/threadimageio/loadsavethread.cpp
@@ -0,0 +1,331 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-12-17
+ * Description : image file IO threaded interface.
+ *
+ * Copyright (C) 2005-2007 by Marcel Wiesweg <marcel.wiesweg@gmx.de>
+ * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+// Local includes.
+
+#include "ddebug.h"
+#include "dmetadata.h"
+#include "loadsavethread.h"
+#include "managedloadsavethread.h"
+#include "sharedloadsavethread.h"
+#include "loadsavetask.h"
+
+namespace Digikam
+{
+
+class LoadSaveThreadPriv
+{
+public:
+
+ LoadSaveThreadPriv()
+ {
+ running = true;
+ blockNotification = false;
+ lastTask = 0;
+ }
+
+ bool running;
+ bool blockNotification;
+ LoadSaveTask *lastTask;
+
+ TQTime notificationTime;
+};
+
+//---------------------------------------------------------------------------------------------------
+
+LoadSaveThread::LoadSaveThread()
+{
+ d = new LoadSaveThreadPriv;
+ m_currentTask = 0;
+ m_notificationPolicy = NotificationPolicyTimeLimited;
+
+ start();
+}
+
+LoadSaveThread::~LoadSaveThread()
+{
+ d->running = false;
+ {
+ TQMutexLocker lock(&m_mutex);
+ m_condVar.wakeAll();
+ }
+
+ wait();
+
+ if (d->lastTask)
+ delete d->lastTask;
+ delete d;
+}
+
+void LoadSaveThread::load(LoadingDescription description)
+{
+ TQMutexLocker lock(&m_mutex);
+ m_todo.append(new LoadingTask(this, description));
+ m_condVar.wakeAll();
+}
+
+void LoadSaveThread::save(DImg &image, const TQString& filePath, const TQString &format)
+{
+ TQMutexLocker lock(&m_mutex);
+ m_todo.append(new SavingTask(this, image, filePath, format));
+ m_condVar.wakeAll();
+}
+
+void LoadSaveThread::run()
+{
+ while (d->running)
+ {
+ {
+ TQMutexLocker lock(&m_mutex);
+ if (d->lastTask)
+ {
+ delete d->lastTask;
+ d->lastTask = 0;
+ }
+ m_currentTask = m_todo.getFirst();
+ if (m_currentTask)
+ {
+ m_todo.removeFirst();
+ if (m_notificationPolicy == NotificationPolicyTimeLimited)
+ {
+ // set timing values so that first event is sent only
+ // after an initial time span.
+ d->notificationTime = TQTime::currentTime();
+ d->blockNotification = true;
+ }
+ }
+ else
+ m_condVar.wait(&m_mutex, 1000);
+ }
+ if (m_currentTask)
+ m_currentTask->execute();
+ }
+}
+
+void LoadSaveThread::taskHasFinished()
+{
+ // This function is called by the tasks before they send their final message.
+ // This is to guarantee the user of the API that at least the final message
+ // is sent after load() has been called. This might not been the case
+ // if m_currentTask is currently loading the same image and a race condition
+ // between the return from execute and the next run of the loop above occurs.
+ TQMutexLocker lock(&m_mutex);
+ d->lastTask = m_currentTask;
+ m_currentTask = 0;
+}
+
+void LoadSaveThread::customEvent(TQCustomEvent *event)
+{
+ if (event->type() == NotifyEvent::notifyEventId())
+ {
+ switch (m_notificationPolicy)
+ {
+ case NotificationPolicyDirect:
+ d->blockNotification = false;
+ break;
+ case NotificationPolicyTimeLimited:
+ break;
+ }
+ ((NotifyEvent *)event)->notify(this);
+ }
+}
+
+void LoadSaveThread::setNotificationPolicy(NotificationPolicy notificationPolicy)
+{
+ m_notificationPolicy = notificationPolicy;
+ d->blockNotification = false;
+}
+
+bool LoadSaveThread::querySendNotifyEvent()
+{
+ // This function is called from the thread to ask for permission to send a notify event.
+ switch (m_notificationPolicy)
+ {
+ case NotificationPolicyDirect:
+ // Note that m_blockNotification is not protected by a mutex. However, if there is a
+ // race condition, the worst case is that one event is not sent, which is no problem.
+ if (d->blockNotification)
+ return false;
+ else
+ {
+ d->blockNotification = true;
+ return true;
+ }
+ break;
+ case NotificationPolicyTimeLimited:
+ // Current default time value: 100 millisecs.
+ if (d->blockNotification)
+ d->blockNotification = d->notificationTime.msecsTo(TQTime::currentTime()) < 100;
+
+ if (d->blockNotification)
+ return false;
+ else
+ {
+ d->notificationTime = TQTime::currentTime();
+ d->blockNotification = true;
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+
+// This is a hack needed to prevent hanging when a TDEProcess-based loader (raw loader)
+// is waiting for the process to finish, but the main thread is waiting
+// for the thread to finish and no TDEProcess events are delivered.
+// Remove when porting to TQt4.
+bool LoadSaveThread::isShuttingDown()
+{
+ // the condition is met after d->running is set to false in the destructor
+ return running() && !d->running;
+}
+
+bool LoadSaveThread::exifRotate(DImg &image, const TQString& filePath)
+{
+ TQVariant attribute(image.attribute("exifRotated"));
+ if (attribute.isValid() && attribute.toBool())
+ return false;
+
+ // Raw files are already rotated properlly by dcraw. Only perform auto-rotation with JPEG/PNG/TIFF file.
+ // We don't have a feedback from dcraw about auto-rotated RAW file during decoding. Return true anyway.
+
+ attribute = image.attribute("fromRawEmbeddedPreview");
+ if (DImg::fileFormat(filePath) == DImg::RAW && !(attribute.isValid() && attribute.toBool()) )
+ {
+ return true;
+ }
+
+ // Rotate thumbnail based on metadata orientation information
+
+ DMetadata metadata(filePath);
+ DMetadata::ImageOrientation orientation = metadata.getImageOrientation();
+
+ bool rotatedOrFlipped = false;
+
+ if(orientation != DMetadata::ORIENTATION_NORMAL)
+ {
+ switch (orientation)
+ {
+ case DMetadata::ORIENTATION_NORMAL:
+ case DMetadata::ORIENTATION_UNSPECIFIED:
+ break;
+
+ case DMetadata::ORIENTATION_HFLIP:
+ image.flip(DImg::HORIZONTAL);
+ rotatedOrFlipped = true;
+ break;
+
+ case DMetadata::ORIENTATION_ROT_180:
+ image.rotate(DImg::ROT180);
+ rotatedOrFlipped = true;
+ break;
+
+ case DMetadata::ORIENTATION_VFLIP:
+ image.flip(DImg::VERTICAL);
+ rotatedOrFlipped = true;
+ break;
+
+ case DMetadata::ORIENTATION_ROT_90_HFLIP:
+ image.rotate(DImg::ROT90);
+ image.flip(DImg::HORIZONTAL);
+ rotatedOrFlipped = true;
+ break;
+
+ case DMetadata::ORIENTATION_ROT_90:
+ image.rotate(DImg::ROT90);
+ rotatedOrFlipped = true;
+ break;
+
+ case DMetadata::ORIENTATION_ROT_90_VFLIP:
+ image.rotate(DImg::ROT90);
+ image.flip(DImg::VERTICAL);
+ rotatedOrFlipped = true;
+ break;
+
+ case DMetadata::ORIENTATION_ROT_270:
+ image.rotate(DImg::ROT270);
+ rotatedOrFlipped = true;
+ break;
+ }
+ }
+
+ image.setAttribute("exifRotated", true);
+ return rotatedOrFlipped;
+
+ /*
+ if (orientation == DMetadata::ORIENTATION_NORMAL ||
+ orientation == DMetadata::ORIENTATION_UNSPECIFIED)
+ return;
+
+ TQWMatrix matrix;
+
+ switch (orientation)
+ {
+ case DMetadata::ORIENTATION_NORMAL:
+ case DMetadata::ORIENTATION_UNSPECIFIED:
+ break;
+
+ case DMetadata::ORIENTATION_HFLIP:
+ matrix.scale(-1, 1);
+ break;
+
+ case DMetadata::ORIENTATION_ROT_180:
+ matrix.rotate(180);
+ break;
+
+ case DMetadata::ORIENTATION_VFLIP:
+ matrix.scale(1, -1);
+ break;
+
+ case DMetadata::ORIENTATION_ROT_90_HFLIP:
+ matrix.scale(-1, 1);
+ matrix.rotate(90);
+ break;
+
+ case DMetadata::ORIENTATION_ROT_90:
+ matrix.rotate(90);
+ break;
+
+ case DMetadata::ORIENTATION_ROT_90_VFLIP:
+ matrix.scale(1, -1);
+ matrix.rotate(90);
+ break;
+
+ case DMetadata::ORIENTATION_ROT_270:
+ matrix.rotate(270);
+ break;
+ }
+
+ // transform accordingly
+ thumb = thumb.xForm( matrix );
+ */
+}
+
+
+
+
+} // namespace Digikam
+
+#include "loadsavethread.moc"
diff --git a/src/libs/threadimageio/loadsavethread.h b/src/libs/threadimageio/loadsavethread.h
new file mode 100644
index 00000000..c935e47a
--- /dev/null
+++ b/src/libs/threadimageio/loadsavethread.h
@@ -0,0 +1,184 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-12-17
+ * Description : image file IO threaded interface.
+ *
+ * Copyright (C) 2005-2006 by Marcel Wiesweg <marcel.wiesweg@gmx.de>
+ * Copyright (C) 2005-2006 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+#ifndef LOAD_SAVE_THREAD_H
+#define LOAD_SAVE_THREAD_H
+
+// TQt includes.
+
+#include <tqthread.h>
+#include <tqobject.h>
+#include <tqmutex.h>
+#include <tqptrlist.h>
+#include <tqdatetime.h>
+#include <tqwaitcondition.h>
+
+// Digikam includes.
+
+#include "dimg.h"
+#include "digikam_export.h"
+#include "loadingdescription.h"
+
+namespace Digikam
+{
+
+class LoadSaveThreadPriv;
+class LoadSaveTask;
+
+class DIGIKAM_EXPORT LoadSaveThread : public TQObject, public TQThread
+{
+
+ TQ_OBJECT
+
+
+public:
+
+ enum NotificationPolicy
+ {
+ /** Always send notification, unless the last event is still in the event queue */
+ NotificationPolicyDirect,
+ /**
+ * Always wait for a certain amount of time after the last event sent.
+ * In particular, the first event will be sent only after waiting for this time span.
+ * (Or no event will be sent, when the loading has finished before)
+ * This is the default.
+ */
+ NotificationPolicyTimeLimited
+ };
+
+ // used by SharedLoadSaveThread only
+ enum AccessMode
+ {
+ // image will only be used for reading
+ AccessModeRead,
+ // image data will possibly be changed
+ AccessModeReadWrite
+ };
+
+ LoadSaveThread();
+ /**
+ * Destructor:
+ * The thread will execute all pending tasks and wait for this upon destruction
+ */
+ ~LoadSaveThread();
+
+ /** Append a task to load the given file to the task list */
+ void load(LoadingDescription description);
+ /** Append a task to save the image to the task list */
+ void save(DImg &image, const TQString& filePath, const TQString &format);
+
+ void setNotificationPolicy(NotificationPolicy notificationPolicy);
+
+ bool isShuttingDown();
+
+ /**
+ * Utility to make sure that an image is rotated according to exif tag.
+ * Detects if an image has previously already been rotated: You can
+ * call this method more than one time on the same image.
+ * Returns true if the image has actually been rotated or flipped.
+ * Returns false if a rotation was not needed.
+ */
+ static bool exifRotate(DImg &image, const TQString& filePath);
+
+signals:
+
+ /** This signal is emitted when the loading process begins. */
+ void signalImageStartedLoading(const LoadingDescription &loadingDescription);
+ /**
+ * This signal is emitted whenever new progress info is available
+ * and the notification policy allows emitting the signal.
+ * No progress info will be sent for preloaded images (ManagedLoadSaveThread).
+ */
+ void signalLoadingProgress(const LoadingDescription &loadingDescription, float progress);
+ /**
+ * This signal is emitted when the loading process has finished.
+ * If the process failed, img is null.
+ */
+ void signalImageLoaded(const LoadingDescription &loadingDescription, const DImg& img);
+ /**
+ * This signal is emitted if
+ * - you are doing shared loading (SharedLoadSaveThread)
+ * - you started a loading operation with a LoadingDescription for
+ * a reduced version of the image
+ * - another thread started a loading operation for a more complete version
+ * You may want to cancel the current operation and start with the given loadingDescription
+ */
+ void signalMoreCompleteLoadingAvailable(const LoadingDescription &oldLoadingDescription,
+ const LoadingDescription &newLoadingDescription);
+
+ void signalImageStartedSaving(const TQString& filePath);
+ void signalSavingProgress(const TQString& filePath, float progress);
+ void signalImageSaved(const TQString& filePath, bool success);
+
+public:
+
+ void imageStartedLoading(const LoadingDescription &loadingDescription)
+ { emit signalImageStartedLoading(loadingDescription); };
+
+ void loadingProgress(const LoadingDescription &loadingDescription, float progress)
+ { emit signalLoadingProgress(loadingDescription, progress); };
+
+ void imageLoaded(const LoadingDescription &loadingDescription, const DImg& img)
+ { emit signalImageLoaded(loadingDescription, img); };
+
+ void moreCompleteLoadingAvailable(const LoadingDescription &oldLoadingDescription,
+ const LoadingDescription &newLoadingDescription)
+ { emit signalMoreCompleteLoadingAvailable(oldLoadingDescription, newLoadingDescription); }
+
+ void imageStartedSaving(const TQString& filePath)
+ { emit signalImageStartedSaving(filePath); };
+
+ void savingProgress(const TQString& filePath, float progress)
+ { emit signalSavingProgress(filePath, progress); };
+
+ void imageSaved(const TQString& filePath, bool success)
+ { emit signalImageSaved(filePath, success); };
+
+ virtual bool querySendNotifyEvent();
+ virtual void taskHasFinished();
+
+protected:
+
+ virtual void run();
+ virtual void customEvent(TQCustomEvent *event);
+
+ TQMutex m_mutex;
+
+ TQWaitCondition m_condVar;
+
+ TQPtrList<LoadSaveTask> m_todo;
+
+ LoadSaveTask *m_currentTask;
+
+ NotificationPolicy m_notificationPolicy;
+
+private:
+
+ LoadSaveThreadPriv* d;
+
+};
+
+} // namespace Digikam
+
+#endif // LOAD_SAVE_THREAD_H
diff --git a/src/libs/threadimageio/managedloadsavethread.cpp b/src/libs/threadimageio/managedloadsavethread.cpp
new file mode 100644
index 00000000..51d9f1b7
--- /dev/null
+++ b/src/libs/threadimageio/managedloadsavethread.cpp
@@ -0,0 +1,362 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2006-01-20
+ * Description : image file IO threaded interface.
+ *
+ * Copyright (C) 2005-2007 by Marcel Wiesweg <marcel.wiesweg@gmx.de>
+ * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+// Local includes.
+
+#include "ddebug.h"
+#include "managedloadsavethread.h"
+#include "loadsavetask.h"
+#include "previewtask.h"
+
+namespace Digikam
+{
+
+ManagedLoadSaveThread::ManagedLoadSaveThread()
+{
+ m_terminationPolicy = TerminationPolicyTerminateLoading;
+}
+
+ManagedLoadSaveThread::~ManagedLoadSaveThread()
+{
+ LoadingTask *loadingTask;
+ switch (m_terminationPolicy)
+ {
+ case TerminationPolicyTerminateLoading:
+ {
+ TQMutexLocker lock(&m_mutex);
+ if ( (loadingTask = checkLoadingTask(m_currentTask, LoadingTaskFilterAll)) )
+ loadingTask->setStatus(LoadingTask::LoadingTaskStatusStopping);
+ removeLoadingTasks(LoadingDescription(TQString()), LoadingTaskFilterAll);
+ break;
+ }
+ case TerminationPolicyTerminatePreloading:
+ {
+ TQMutexLocker lock(&m_mutex);
+ if ( (loadingTask = checkLoadingTask(m_currentTask, LoadingTaskFilterPreloading)) )
+ loadingTask->setStatus(LoadingTask::LoadingTaskStatusStopping);
+ removeLoadingTasks(LoadingDescription(TQString()), LoadingTaskFilterPreloading);
+ break;
+ }
+ case TerminationPolicyWait:
+ break;
+ }
+}
+
+LoadingTask *ManagedLoadSaveThread::checkLoadingTask(LoadSaveTask *task, LoadingTaskFilter filter)
+{
+ if (task && task->type() == LoadSaveTask::TaskTypeLoading)
+ {
+ LoadingTask *loadingTask = (LoadingTask *)task;
+ if (filter == LoadingTaskFilterAll)
+ return loadingTask;
+ else if (filter == LoadingTaskFilterPreloading)
+ if (loadingTask->status() == LoadingTask::LoadingTaskStatusPreloading)
+ return loadingTask;
+ }
+ return 0;
+}
+
+LoadingTask *ManagedLoadSaveThread::findExistingTask(const LoadingDescription &loadingDescription)
+{
+ LoadingTask *loadingTask;
+ if (m_currentTask)
+ {
+ if (m_currentTask->type() == LoadSaveTask::TaskTypeLoading)
+ {
+ loadingTask = (LoadingTask *)m_currentTask;
+ LoadingDescription taskDescription = loadingTask->loadingDescription();
+ if (taskDescription == loadingDescription)
+ return loadingTask;
+ }
+ }
+ for (LoadSaveTask *task = m_todo.first(); task; task = m_todo.next())
+ {
+ if (task->type() == LoadSaveTask::TaskTypeLoading)
+ {
+ loadingTask = (LoadingTask *)task;
+ if (loadingTask->loadingDescription() == loadingDescription)
+ return loadingTask;
+ }
+ }
+ return 0;
+}
+
+void ManagedLoadSaveThread::setTerminationPolicy(TerminationPolicy terminationPolicy)
+{
+ m_terminationPolicy = terminationPolicy;
+}
+
+void ManagedLoadSaveThread::load(LoadingDescription description, LoadingPolicy policy)
+{
+ load(description, LoadingModeNormal, policy);
+}
+
+void ManagedLoadSaveThread::load(LoadingDescription description, LoadingMode loadingMode, LoadingPolicy policy, AccessMode accessMode)
+{
+ TQMutexLocker lock(&m_mutex);
+ LoadingTask *loadingTask = 0;
+ LoadingTask *existingTask = findExistingTask(description);
+
+ //DDebug() << "ManagedLoadSaveThread::load " << description.filePath << ", policy " << policy << endl;
+ switch(policy)
+ {
+ case LoadingPolicyFirstRemovePrevious:
+ // reuse task if it exists
+ if (existingTask)
+ {
+ existingTask->setStatus(LoadingTask::LoadingTaskStatusLoading);
+ }
+ // stop current task
+ if (m_currentTask && m_currentTask != existingTask)
+ {
+ if ( (loadingTask = checkLoadingTask(m_currentTask, LoadingTaskFilterAll)) )
+ loadingTask->setStatus(LoadingTask::LoadingTaskStatusStopping);
+ }
+ //DDebug() << "LoadingPolicyFirstRemovePrevious, Existing task " << existingTask <<
+ //", m_currentTask " << m_currentTask << ", loadingTask " << loadingTask << endl;
+ // remove all loading tasks
+ for (LoadSaveTask *task = m_todo.first(); task; task = m_todo.next())
+ {
+ if (task != existingTask && checkLoadingTask(task, LoadingTaskFilterAll))
+ {
+ //DDebug() << "Removing task " << task << " from list" << endl;
+ m_todo.remove();
+ m_todo.prev();
+ }
+ }
+ // append new, exclusive loading task
+ if (existingTask)
+ break;
+ m_todo.append(createLoadingTask(description, false, loadingMode, accessMode));
+ break;
+ case LoadingPolicyPrepend:
+ if (existingTask)
+ {
+ existingTask->setStatus(LoadingTask::LoadingTaskStatusLoading);
+ }
+ // stop and postpone current task if it is a preloading task
+ if (m_currentTask)
+ {
+ if ( (loadingTask = checkLoadingTask(m_currentTask, LoadingTaskFilterPreloading)) )
+ {
+ loadingTask->setStatus(LoadingTask::LoadingTaskStatusStopping);
+ load(loadingTask->filePath(), LoadingPolicyPreload);
+ }
+ }
+ //DDebug() << "LoadingPolicyPrepend, Existing task " << existingTask << ", m_currentTask " << m_currentTask << endl;
+ // prepend new loading task
+ if (existingTask)
+ break;
+ m_todo.prepend(createLoadingTask(description, false, loadingMode, accessMode));
+ break;
+ case LoadingPolicyAppend:
+ if (existingTask)
+ {
+ existingTask->setStatus(LoadingTask::LoadingTaskStatusLoading);
+ }
+ // stop and postpone current task if it is a preloading task
+ if (m_currentTask)
+ {
+ if ( (loadingTask = checkLoadingTask(m_currentTask, LoadingTaskFilterPreloading)) )
+ {
+ loadingTask->setStatus(LoadingTask::LoadingTaskStatusStopping);
+ load(loadingTask->filePath(), LoadingPolicyPreload);
+ }
+ }
+ if (existingTask)
+ break;
+ //DDebug() << "LoadingPolicyAppend, Existing task " << existingTask << ", m_currentTask " << m_currentTask << endl;
+ // append new loading task, put it in front of preloading tasks
+ for (uint i = 0; i<m_todo.count(); i++)
+ {
+ LoadSaveTask *task = m_todo.at(i);
+ if ( (loadingTask = checkLoadingTask(task, LoadingTaskFilterPreloading)) )
+ {
+ m_todo.insert(i, createLoadingTask(description, false, loadingMode, accessMode));
+ break;
+ }
+ }
+ break;
+ case LoadingPolicyPreload:
+ // append to the very end of the list
+ //DDebug() << "LoadingPolicyPreload, Existing task " << existingTask << endl;
+ if (existingTask)
+ break;
+ m_todo.append(createLoadingTask(description, true, loadingMode, accessMode));
+ break;
+ }
+ m_condVar.wakeAll();
+}
+
+void ManagedLoadSaveThread::loadPreview(LoadingDescription description)
+{
+ // This is similar to the LoadingPolicyFirstRemovePrevious policy with normal loading tasks.
+ // Preview threads typically only support preview tasks,
+ // so no need to differentiate with normal loading tasks.
+
+ TQMutexLocker lock(&m_mutex);
+ LoadingTask *loadingTask = 0;
+ LoadingTask *existingTask = findExistingTask(description);
+
+ // reuse task if it exists
+ if (existingTask)
+ {
+ existingTask->setStatus(LoadingTask::LoadingTaskStatusLoading);
+ }
+ // stop current task
+ if (m_currentTask && m_currentTask != existingTask)
+ {
+ if ( (loadingTask = checkLoadingTask(m_currentTask, LoadingTaskFilterAll)) )
+ loadingTask->setStatus(LoadingTask::LoadingTaskStatusStopping);
+ }
+ // remove all loading tasks
+ for (LoadSaveTask *task = m_todo.first(); task; task = m_todo.next())
+ {
+ if (task != existingTask && checkLoadingTask(task, LoadingTaskFilterAll))
+ {
+ m_todo.remove();
+ m_todo.prev();
+ }
+ }
+ //DDebug()<<"loadPreview for " << description.filePath << " existingTask " << existingTask << " currentTask " << m_currentTask << endl;
+ // append new loading task
+ if (existingTask)
+ return;
+ m_todo.append(new PreviewLoadingTask(this, description));
+ m_condVar.wakeAll();
+}
+
+
+LoadingTask *ManagedLoadSaveThread::createLoadingTask(const LoadingDescription &description,
+ bool preloading, LoadingMode loadingMode, AccessMode accessMode)
+{
+ if (loadingMode == LoadingModeShared)
+ {
+ if (preloading)
+ return new SharedLoadingTask(this, description, accessMode, LoadingTask::LoadingTaskStatusPreloading);
+ else
+ return new SharedLoadingTask(this, description, accessMode);
+ }
+ else
+ {
+ if (preloading)
+ return new LoadingTask(this, description, LoadingTask::LoadingTaskStatusPreloading);
+ else
+ return new LoadingTask(this, description);
+ }
+}
+
+void ManagedLoadSaveThread::stopLoading(const TQString& filePath, LoadingTaskFilter filter)
+{
+ TQMutexLocker lock(&m_mutex);
+ removeLoadingTasks(LoadingDescription(filePath), filter);
+}
+
+void ManagedLoadSaveThread::stopLoading(const LoadingDescription& desc, LoadingTaskFilter filter)
+{
+ TQMutexLocker lock(&m_mutex);
+ removeLoadingTasks(desc, filter);
+}
+
+void ManagedLoadSaveThread::stopSaving(const TQString& filePath)
+{
+ TQMutexLocker lock(&m_mutex);
+
+ // stop current task if it is matching the criteria
+ if (m_currentTask && m_currentTask->type() == LoadSaveTask::TaskTypeSaving)
+ {
+ SavingTask *savingTask = (SavingTask *)m_currentTask;
+ if (filePath.isNull() || savingTask->filePath() == filePath)
+ {
+ savingTask->setStatus(SavingTask::SavingTaskStatusStopping);
+ }
+ }
+
+ // remove relevant tasks from list
+ for (LoadSaveTask *task = m_todo.first(); task; task = m_todo.next())
+ {
+ if (task->type() == LoadSaveTask::TaskTypeSaving)
+ {
+ SavingTask *savingTask = (SavingTask *)m_currentTask;
+ if (filePath.isNull() || savingTask->filePath() == filePath)
+ {
+ m_todo.remove();
+ m_todo.prev();
+ }
+ }
+ }
+}
+
+
+void ManagedLoadSaveThread::removeLoadingTasks(const LoadingDescription &description, LoadingTaskFilter filter)
+{
+ LoadingTask *loadingTask;
+
+ // stop current task if it is matching the criteria
+ if ( (loadingTask = checkLoadingTask(m_currentTask, filter)) )
+ {
+ if (description.filePath.isNull() || loadingTask->loadingDescription() == description)
+ {
+ loadingTask->setStatus(LoadingTask::LoadingTaskStatusStopping);
+ }
+ }
+
+ // remove relevant tasks from list
+ for (LoadSaveTask *task = m_todo.first(); task; task = m_todo.next())
+ {
+ if ( (loadingTask = checkLoadingTask(task, filter)) )
+ {
+ if (description.filePath.isNull() || loadingTask->loadingDescription() == description)
+ {
+ m_todo.remove();
+ m_todo.prev();
+ }
+ }
+ }
+}
+
+void ManagedLoadSaveThread::save(DImg &image, const TQString& filePath, const TQString &format)
+{
+ TQMutexLocker lock(&m_mutex);
+ LoadingTask *loadingTask;
+
+ // stop and postpone current task if it is a preloading task
+ if (m_currentTask && (loadingTask = checkLoadingTask(m_currentTask, LoadingTaskFilterPreloading)))
+ {
+ loadingTask->setStatus(LoadingTask::LoadingTaskStatusStopping);
+ load(loadingTask->filePath(), LoadingPolicyPreload);
+ }
+ // append new loading task, put it in front of preloading tasks
+ uint i;
+ for (i = 0; i<m_todo.count(); i++)
+ {
+ LoadSaveTask *task = m_todo.at(i);
+ if ( (loadingTask = checkLoadingTask(task, LoadingTaskFilterPreloading)) )
+ break;
+ }
+ m_todo.insert(i, new SavingTask(this, image, filePath, format));
+}
+
+} // namespace Digikam
+
diff --git a/src/libs/threadimageio/managedloadsavethread.h b/src/libs/threadimageio/managedloadsavethread.h
new file mode 100644
index 00000000..ec6a563c
--- /dev/null
+++ b/src/libs/threadimageio/managedloadsavethread.h
@@ -0,0 +1,134 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2006-01-16
+ * Description : image file IO threaded interface.
+ *
+ * Copyright (C) 2005-2007 by Marcel Wiesweg <marcel.wiesweg@gmx.de>
+ * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+#ifndef MANAGED_LOAD_SAVE_THREAD_H
+#define MANAGED_LOAD_SAVE_THREAD_H
+
+#include "loadsavethread.h"
+
+namespace Digikam
+{
+
+class LoadingTask;
+
+class DIGIKAM_EXPORT ManagedLoadSaveThread : public LoadSaveThread
+{
+
+public:
+
+ // Termination is controlled by setting the TerminationPolicy
+ // Default is TerminationPolicyTerminateLoading
+ ManagedLoadSaveThread();
+ ~ManagedLoadSaveThread();
+
+ enum LoadingPolicy
+ {
+ // Load image immediately, remove and stop all previous loading tasks.
+ LoadingPolicyFirstRemovePrevious,
+ // Prepend loading in front of all other tasks, but wait for the current task to finish.
+ // No other tasks will be removed, preloading tasks will be stopped and postponed.
+ LoadingPolicyPrepend,
+ // Append loading task to the end of the list, but in front of all preloading tasks.
+ // No other tasks will be removed, preloading tasks will be stopped and postponed.
+ // This is similar to the simple load() operation from LoadSaveThread, except for the
+ // special care taken for preloading.
+ LoadingPolicyAppend,
+ // Preload image, i.e. load it with low priority when no other tasks are scheduled.
+ // All other tasks will take precedence, and preloading tasks will be stopped and
+ // postponed when another task is added.
+ // No progress info will be sent for preloaded images
+ LoadingPolicyPreload
+ };
+
+ enum TerminationPolicy
+ {
+ // Wait for saving tasks, stop and remove loading tasks
+ // This is the default.
+ TerminationPolicyTerminateLoading,
+ // Wait for loading and saving tasks, stop and remove preloading tasks
+ TerminationPolicyTerminatePreloading,
+ // Wait for all pending tasks
+ TerminationPolicyWait
+ };
+
+ enum LoadingTaskFilter
+ {
+ // filter all loading tasks
+ LoadingTaskFilterAll,
+ // filter only tasks with preloading policy
+ LoadingTaskFilterPreloading
+ };
+
+ // used by SharedLoadSaveThread only
+ enum LoadingMode
+ {
+ // no sharing of loading process, no caching of image
+ LoadingModeNormal,
+ // loading process is shared, image is cached
+ LoadingModeShared
+ };
+
+ // Append a task to load the given file to the task list.
+ // If there is already a task for the given file, it will possibly be rescheduled,
+ // but no second task will be added.
+ // Only loading tasks will - if required by the policy - be stopped or removed,
+ // saving tasks will not be touched.
+ void load(LoadingDescription description, LoadingPolicy policy = LoadingPolicyAppend);
+
+ // Stop and remove tasks filtered by filePath and policy.
+ // If filePath isNull, applies to all file paths.
+ void stopLoading(const TQString& filePath = TQString(), LoadingTaskFilter filter = LoadingTaskFilterAll);
+
+ // Same than previous method, but Stop and remove tasks filtered by LoadingDescription.
+ void stopLoading(const LoadingDescription& desc, LoadingTaskFilter filter = LoadingTaskFilterAll);
+
+ // Stop and remove saving tasks filtered by filePath.
+ // If filePath isNull, applies to all file paths.
+ void stopSaving(const TQString& filePath = TQString());
+
+ // Append a task to save the image to the task list
+ void save(DImg &image, const TQString& filePath, const TQString &format);
+
+ void setTerminationPolicy(TerminationPolicy terminationPolicy);
+
+protected:
+
+ void load(LoadingDescription description, LoadingMode loadingMode,
+ LoadingPolicy policy = LoadingPolicyAppend, AccessMode mode = AccessModeReadWrite);
+ void loadPreview(LoadingDescription description);
+
+private:
+
+ LoadingTask *checkLoadingTask(class LoadSaveTask *task, LoadingTaskFilter filter);
+ LoadingTask *findExistingTask(const LoadingDescription &description);
+ LoadingTask *createLoadingTask(const LoadingDescription &description, bool preloading, LoadingMode loadingMode, AccessMode accessMode);
+ void removeLoadingTasks(const LoadingDescription &description, LoadingTaskFilter filter);
+
+ TerminationPolicy m_terminationPolicy;
+};
+
+} // namespace Digikam
+
+
+#endif // MANAGED_LOAD_SAVE_THREAD_H
diff --git a/src/libs/threadimageio/previewloadthread.cpp b/src/libs/threadimageio/previewloadthread.cpp
new file mode 100644
index 00000000..d243ab32
--- /dev/null
+++ b/src/libs/threadimageio/previewloadthread.cpp
@@ -0,0 +1,52 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2006-01-20
+ * Description : image file IO threaded interface.
+ *
+ * Copyright (C) 2005-2007 by Marcel Wiesweg <marcel.wiesweg@gmx.de>
+ * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+// Local includes.
+
+#include "previewtask.h"
+#include "previewloadthread.h"
+#include "previewloadthread.moc"
+
+namespace Digikam
+{
+
+PreviewLoadThread::PreviewLoadThread()
+{
+}
+
+void PreviewLoadThread::load(LoadingDescription description)
+{
+ description.rawDecodingSettings.sixteenBitsImage = false;
+ ManagedLoadSaveThread::loadPreview(description);
+}
+
+void PreviewLoadThread::loadHighQuality(LoadingDescription description)
+{
+ description.rawDecodingSettings.optimizeTimeLoading();
+ description.rawDecodingSettings.sixteenBitsImage = false;
+ ManagedLoadSaveThread::load(description, LoadingModeShared, LoadingPolicyFirstRemovePrevious);
+}
+
+} // namespace Digikam
+
diff --git a/src/libs/threadimageio/previewloadthread.h b/src/libs/threadimageio/previewloadthread.h
new file mode 100644
index 00000000..59cc6d72
--- /dev/null
+++ b/src/libs/threadimageio/previewloadthread.h
@@ -0,0 +1,57 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2006-01-16
+ * Description : image file IO threaded interface.
+ *
+ * Copyright (C) 2006-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.
+ *
+ * ============================================================ */
+
+#ifndef PREVIEW_LOAD_THREAD_H
+#define PREVIEW_LOAD_THREAD_H
+
+#include "managedloadsavethread.h"
+
+namespace Digikam
+{
+
+class DIGIKAM_EXPORT PreviewLoadThread : public ManagedLoadSaveThread
+{
+ TQ_OBJECT
+
+
+public:
+
+ PreviewLoadThread();
+
+ /**
+ * Load a preview that is optimized for fast loading.
+ */
+ void load(LoadingDescription description);
+ /**
+ * Load a preview with higher resolution, trading more quality
+ * for less speed.
+ * In the LoadingDescription container, provide "0" as maximum size.
+ */
+ void loadHighQuality(LoadingDescription description);
+
+};
+
+} // namespace Digikam
+
+
+#endif // SHARED_LOAD_SAVE_THREAD_H
diff --git a/src/libs/threadimageio/previewtask.cpp b/src/libs/threadimageio/previewtask.cpp
new file mode 100644
index 00000000..5f277481
--- /dev/null
+++ b/src/libs/threadimageio/previewtask.cpp
@@ -0,0 +1,258 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2006-12-26
+ * Description : Multithreaded loader for previews
+ *
+ * Copyright (C) 2006-2007 by Marcel Wiesweg <marcel.wiesweg@gmx.de>
+ * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+// C++ includes.
+
+#include <cmath>
+
+// TQt includes.
+
+#include <tqapplication.h>
+#include <tqimage.h>
+#include <tqvariant.h>
+#include <tqwmatrix.h>
+
+// LibKDcraw includes.
+
+#include <libkdcraw/kdcraw.h>
+
+// Local includes.
+
+#include "ddebug.h"
+#include "dmetadata.h"
+#include "jpegutils.h"
+#include "previewloadthread.h"
+#include "previewtask.h"
+
+namespace Digikam
+{
+
+void PreviewLoadingTask::execute()
+{
+ if (m_loadingTaskStatus == LoadingTaskStatusStopping)
+ return;
+
+ LoadingCache *cache = LoadingCache::cache();
+ {
+ LoadingCache::CacheLock lock(cache);
+
+ // find possible cached images
+ DImg *cachedImg = 0;
+ TQStringList lookupKeys = m_loadingDescription.lookupCacheKeys();
+ // lookupCacheKeys returns "best first". Prepend the cache key to make the list "fastest first":
+ // Scaling a full version takes longer!
+ lookupKeys.push_front(m_loadingDescription.cacheKey());
+ for ( TQStringList::Iterator it = lookupKeys.begin(); it != lookupKeys.end(); ++it ) {
+ if ( (cachedImg = cache->retrieveImage(*it)) )
+ break;
+ }
+
+ if (cachedImg)
+ {
+ // image is found in image cache, loading is successful
+
+ DImg img(*cachedImg);
+
+ // rotate if needed - images are unrotated in the cache,
+ // except for RAW images, which are already rotated by dcraw.
+ if (m_loadingDescription.previewParameters.exifRotate)
+ {
+ img = img.copy();
+ LoadSaveThread::exifRotate(img, m_loadingDescription.filePath);
+ }
+
+ TQApplication::postEvent(m_thread, new LoadedEvent(m_loadingDescription.filePath, img));
+ return;
+ }
+ else
+ {
+ // find possible running loading process
+ m_usedProcess = 0;
+ for ( TQStringList::Iterator it = lookupKeys.begin(); it != lookupKeys.end(); ++it ) {
+ if ( (m_usedProcess = cache->retrieveLoadingProcess(*it)) )
+ {
+ break;
+ }
+ }
+ // do not wait on other loading processes?
+ //m_usedProcess = cache->retrieveLoadingProcess(m_loadingDescription.cacheKey());
+
+ if (m_usedProcess)
+ {
+ // Other process is right now loading this image.
+ // Add this task to the list of listeners and
+ // attach this thread to the other thread, wait until loading
+ // has finished.
+ m_usedProcess->addListener(this);
+ // break loop when either the loading has completed, or this task is being stopped
+ while ( !m_usedProcess->completed() && m_loadingTaskStatus != LoadingTaskStatusStopping )
+ lock.timedWait();
+ // remove listener from process
+ m_usedProcess->removeListener(this);
+ // wake up the process which is waiting until all listeners have removed themselves
+ lock.wakeAll();
+ // set to 0, as checked in setStatus
+ m_usedProcess = 0;
+ return;
+ }
+ else
+ {
+ // Neither in cache, nor currently loading in different thread.
+ // Load it here and now, add this LoadingProcess to cache list.
+ cache->addLoadingProcess(this);
+ // Add this to the list of listeners
+ addListener(this);
+ // for use in setStatus
+ m_usedProcess = this;
+ // Notify other processes that we are now loading this image.
+ // They might be interested - see notifyNewLoadingProcess below
+ cache->notifyNewLoadingProcess(this, m_loadingDescription);
+ }
+ }
+ }
+
+ // load image
+ int size = m_loadingDescription.previewParameters.size;
+
+ DImg img;
+ TQImage qimage;
+ bool fromEmbeddedPreview = false;
+
+ // -- Get the image preview --------------------------------
+
+ // First the TQImage-dependent loading methods
+ // Trying to load with dcraw: RAW files.
+ if (KDcrawIface::KDcraw::loadEmbeddedPreview(qimage, m_loadingDescription.filePath))
+ fromEmbeddedPreview = true;
+
+ if (qimage.isNull())
+ {
+ //TODO: Use DImg based loader instead?
+ KDcrawIface::KDcraw::loadHalfPreview(qimage, m_loadingDescription.filePath);
+ }
+
+ // Try to extract Exif/Iptc preview.
+ if (qimage.isNull())
+ {
+ loadImagePreview(qimage, m_loadingDescription.filePath);
+ }
+
+ if (!qimage.isNull())
+ {
+ // convert from TQImage
+ img = DImg(qimage);
+ // mark as embedded preview (for exif rotation)
+ if (fromEmbeddedPreview)
+ img.setAttribute("fromRawEmbeddedPreview", true);
+ // free memory
+ qimage = TQImage();
+ }
+
+ // DImg-dependent loading methods
+ if (img.isNull())
+ {
+ // Set a hint to try to load a JPEG with the fast scale-before-decoding method
+ img.setAttribute("jpegScaledLoadingSize", size);
+ img.load(m_loadingDescription.filePath, this, m_loadingDescription.rawDecodingSettings);
+ }
+
+ if (img.isNull())
+ {
+ DWarning() << "Cannot extract preview for " << m_loadingDescription.filePath << endl;
+ }
+
+ img.convertToEightBit();
+
+ // Reduce size of image:
+ // - only scale down if size is considerably larger
+ // - only scale down, do not scale up
+ TQSize scaledSize = img.size();
+ if (needToScale(scaledSize, size))
+ {
+ scaledSize.scale(size, size, TQSize::ScaleMin);
+ img = img.smoothScale(scaledSize.width(), scaledSize.height());
+ }
+
+ // Scale if hinted, Store previews rotated in the cache (?)
+ if (m_loadingDescription.previewParameters.exifRotate)
+ LoadSaveThread::exifRotate(img, m_loadingDescription.filePath);
+
+ {
+ LoadingCache::CacheLock lock(cache);
+ // put (valid) image into cache of loaded images
+ if (!img.isNull())
+ cache->putImage(m_loadingDescription.cacheKey(), new DImg(img), m_loadingDescription.filePath);
+ // remove this from the list of loading processes in cache
+ cache->removeLoadingProcess(this);
+ }
+
+ // following the golden rule to avoid deadlocks, do this when CacheLock is not held
+ m_thread->taskHasFinished();
+
+ {
+ LoadingCache::CacheLock lock(cache);
+ // indicate that loading has finished so that listeners can stop waiting
+ m_completed = true;
+
+ // dispatch image to all listeners, including this
+ for (LoadingProcessListener *l = m_listeners.first(); l; l = m_listeners.next())
+ {
+ TQApplication::postEvent(l->eventReceiver(), new LoadedEvent(m_loadingDescription, img));
+ }
+
+ // remove myself from list of listeners
+ removeListener(this);
+ // wake all listeners waiting on cache condVar, so that they remove themselves
+ lock.wakeAll();
+ // wait until all listeners have removed themselves
+ while (m_listeners.count() != 0)
+ lock.timedWait();
+ // set to 0, as checked in setStatus
+ m_usedProcess = 0;
+ }
+}
+
+bool PreviewLoadingTask::needToScale(const TQSize &imageSize, int previewSize)
+{
+ int maxSize = imageSize.width() > imageSize.height() ? imageSize.width() : imageSize.height();
+ int acceptableUpperSize = lround(1.25 * (double)previewSize);
+ return maxSize >= acceptableUpperSize;
+}
+
+// -- Exif/IPTC preview extraction using Exiv2 --------------------------------------------------------
+
+bool PreviewLoadingTask::loadImagePreview(TQImage& image, const TQString& path)
+{
+ DMetadata metadata(path);
+ if (metadata.getImagePreview(image))
+ {
+ DDebug() << "Use Exif/Iptc preview extraction. Size of image: "
+ << image.width() << "x" << image.height() << endl;
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace Digikam
diff --git a/src/libs/threadimageio/previewtask.h b/src/libs/threadimageio/previewtask.h
new file mode 100644
index 00000000..f5715499
--- /dev/null
+++ b/src/libs/threadimageio/previewtask.h
@@ -0,0 +1,57 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2006-12-26
+ * Description : Multithreaded loader for previews
+ *
+ * Copyright (C) 2006-2007 by Marcel Wiesweg <marcel.wiesweg@gmx.de>
+ * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+#ifndef PREVIEW_TASK_H
+#define PREVIEW_TASK_H
+
+// TQt includes.
+
+#include <tqimage.h>
+
+// Local includes.
+
+#include "loadsavetask.h"
+
+namespace Digikam
+{
+
+class PreviewLoadingTask : public SharedLoadingTask
+{
+public:
+
+ PreviewLoadingTask(LoadSaveThread* thread, LoadingDescription description)
+ : SharedLoadingTask(thread, description, LoadSaveThread::AccessModeRead, LoadingTaskStatusLoading)
+ {}
+
+ virtual void execute();
+
+private:
+
+ bool needToScale(const TQSize &imageSize, int previewSize);
+ bool loadImagePreview(TQImage& image, const TQString& path);
+};
+
+} // namespace Digikam
+
+#endif // PREVIEW_TASK_H
diff --git a/src/libs/threadimageio/sharedloadsavethread.cpp b/src/libs/threadimageio/sharedloadsavethread.cpp
new file mode 100644
index 00000000..6a9cccbf
--- /dev/null
+++ b/src/libs/threadimageio/sharedloadsavethread.cpp
@@ -0,0 +1,65 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2006-01-20
+ * Description : image file IO threaded interface.
+ *
+ * Copyright (C) 2005-2007 by Marcel Wiesweg <marcel.wiesweg@gmx.de>
+ * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+// Local includes.
+
+#include "ddebug.h"
+#include "sharedloadsavethread.h"
+#include "loadingcache.h"
+#include "loadsavetask.h"
+
+namespace Digikam
+{
+
+void SharedLoadSaveThread::load(LoadingDescription description, AccessMode mode, LoadingPolicy policy)
+{
+ ManagedLoadSaveThread::load(description, LoadingModeShared, policy, mode);
+}
+
+DImg SharedLoadSaveThread::cacheLookup(const TQString& filePath, AccessMode /*accessMode*/)
+{
+ LoadingCache *cache = LoadingCache::cache();
+ LoadingCache::CacheLock lock(cache);
+ DImg *cachedImg = cache->retrieveImage(filePath);
+ // TQt4: uncomment this code.
+ // See comments in SharedLoadingTask::execute for explanation.
+ /*
+ if (cachedImg)
+ {
+ if (accessMode == AccessModeReadWrite)
+ return cachedImg->copy();
+ else
+ return *cachedImg;
+ }
+ else
+ return DImg();
+ */
+ if (cachedImg)
+ return cachedImg->copy();
+ else
+ return DImg();
+}
+
+} // namespace Digikam
+
diff --git a/src/libs/threadimageio/sharedloadsavethread.h b/src/libs/threadimageio/sharedloadsavethread.h
new file mode 100644
index 00000000..aa03a708
--- /dev/null
+++ b/src/libs/threadimageio/sharedloadsavethread.h
@@ -0,0 +1,43 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2006-01-16
+ * Description : image file IO threaded interface.
+ *
+ * Copyright (C) 2006-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.
+ *
+ * ============================================================ */
+
+#ifndef SHARED_LOAD_SAVE_THREAD_H
+#define SHARED_LOAD_SAVE_THREAD_H
+
+#include "managedloadsavethread.h"
+
+namespace Digikam
+{
+
+class DIGIKAM_EXPORT SharedLoadSaveThread : public ManagedLoadSaveThread
+{
+public:
+
+ void load(LoadingDescription description, AccessMode mode, LoadingPolicy policy = LoadingPolicyAppend);
+ DImg cacheLookup(const TQString& filePath, AccessMode mode);
+};
+
+} // namespace Digikam
+
+
+#endif // SHARED_LOAD_SAVE_THREAD_H