diff --git a/core/libs/threadimageio/fileio/loadingcache.h b/core/libs/threadimageio/fileio/loadingcache.h index fbcaea0e00..e25e4be654 100644 --- a/core/libs/threadimageio/fileio/loadingcache.h +++ b/core/libs/threadimageio/fileio/loadingcache.h @@ -1,313 +1,313 @@ /* ============================================================ * * 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-2011 by Marcel Wiesweg * * 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 DIGIKAM_LOADING_CACHE_H #define DIGIKAM_LOADING_CACHE_H // Qt includes #include #include #include // Local includes #include "dimg.h" #include "loadsavethread.h" #include "digikam_export.h" namespace Digikam { class ICCSettingsContainer; class LoadingProcessListener { public: virtual ~LoadingProcessListener() {}; virtual bool querySendNotifyEvent() const = 0; virtual void setResult(const LoadingDescription& loadingDescription, const DImg& img) = 0; virtual LoadSaveNotifier* loadSaveNotifier() const = 0; virtual LoadSaveThread::AccessMode accessMode() = 0; }; // -------------------------------------------------------------------------------------------------------------- class LoadingProcess { public: virtual ~LoadingProcess() {}; - virtual bool completed() = 0; + virtual bool completed() const volatile = 0; virtual QString filePath() const = 0; virtual QString cacheKey() const = 0; virtual void addListener(LoadingProcessListener* const listener) = 0; virtual void removeListener(LoadingProcessListener* const listener) = 0; virtual void notifyNewLoadingProcess(LoadingProcess* const process, const LoadingDescription& description) = 0; }; // -------------------------------------------------------------------------------------------------------------- class DIGIKAM_EXPORT LoadingCacheFileWatch { public: virtual ~LoadingCacheFileWatch(); /// Called by the thread when a new entry is added to the cache virtual void addedImage(const QString& filePath); virtual void addedThumbnail(const QString& filePath); protected: /** * Convenience method. * Call this to tell the cache to remove stored images for filePath from the cache. * Calling this method is fast, you do not need to check if the file is contained in the cache. * Do not hold the CacheLock when calling this method. */ void notifyFileChanged(const QString& filePath); protected: friend class LoadingCache; class LoadingCache* m_cache; }; // -------------------------------------------------------------------------------------------------------------- class DIGIKAM_EXPORT ClassicLoadingCacheFileWatch : public QObject, public LoadingCacheFileWatch { Q_OBJECT /** Reference implementation */ public: ClassicLoadingCacheFileWatch(); ~ClassicLoadingCacheFileWatch(); virtual void addedImage(const QString& filePath); virtual void addedThumbnail(const QString& filePath); private Q_SLOTS: void slotFileDirty(const QString& path); void slotUpdateDirWatch(); Q_SIGNALS: void signalUpdateDirWatch(); protected: QFileSystemWatcher* m_watch; QSet m_watchedFiles; }; // -------------------------------------------------------------------------------------------------------------- class DIGIKAM_EXPORT LoadingCache : public QObject { Q_OBJECT public: static LoadingCache* cache(); static void cleanUp(); virtual ~LoadingCache(); /// !! All methods of LoadingCache shall only be called when a CacheLock is held !! class DIGIKAM_EXPORT CacheLock { public: explicit 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 QString& cacheKey) const; /// Returns whether the given DImg fits in the cache. bool isCacheable(const DImg* img) const; /** 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 QString& cacheKey, DImg* img, const QString& filePath) const; /** * Remove entries for the given cacheKey from the cache */ void removeImage(const QString& cacheKey); /** * Remove all entries from the cache */ void removeImages(); // ------- Loading process management ----------------------------------- /** * Find the loading process for given cacheKey, or 0 if not found */ LoadingProcess* retrieveLoadingProcess(const QString& cacheKey) const; /** * 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, const LoadingDescription& description); /** * Sets the cache size in megabytes. * The thumbnail cache is not affected and setThumbnailCacheSize takes the maximum number. */ void setCacheSize(int megabytes); // ------- Thumbnail cache ----------------------------------- /// The LoadingCache support both the caching of QImage and QPixmap objects. /// QPixmaps can only be accessed from the main thread, so the tasks cannot access this cache. /** * Retrieves a thumbnail for the given filePath from the thumbnail cache, * or a 0 if the thumbnail is not found. */ const QImage* retrieveThumbnail(const QString& cacheKey) const; const QPixmap* retrieveThumbnailPixmap(const QString& cacheKey) const; bool hasThumbnailPixmap(const QString& cacheKey) const; /** * Puts a thumbnail into the thumbnail cache. */ void putThumbnail(const QString& cacheKey, const QImage& thumb, const QString& filePath); void putThumbnail(const QString& cacheKey, const QPixmap& thumb, const QString& filePath); /** * Remove the thumbnail for the given file path from the thumbnail cache */ void removeThumbnail(const QString& cacheKey); /** * Remove all thumbnails */ void removeThumbnails(); /** * Sets the size of the thumbnail cache * @param numberOfQImages The maximum number of thumbnails of max possible size in QImage format that will be cached. If the size of the images is smaller, a larger number will be cached. * @param numberOfQPixmaps The maximum number of thumbnails of max possible size in QPixmap format that will be cached. If the size of the images is smaller, a larger number will be cached. * Note: The main cache is unaffected by this method, * and setCacheSize takes megabytes as parameter. * Note: A good caching strategy will be to set one of the numbers to 0 * Default values: (0, 100) */ void setThumbnailCacheSize(int numberOfQImages, int numberOfQPixmaps); // ------- File Watch Management ----------------------------------- /** * Sets a LoadingCacheFileWatch to watch the files contained in this cache. * Ownership of this object is transferred to the cache. */ void setFileWatch(LoadingCacheFileWatch* const watch); /** * Returns a list of all possible file paths in cache. */ QStringList imageFilePathsInCache() const; QStringList thumbnailFilePathsInCache() const; /** * Remove all entries from cache that were loaded from filePath. * Emits relevant signals if notify = true. */ void notifyFileChanged(const QString& filePath, bool notify = true); Q_SIGNALS: /** * This signal is emitted when the cache is notified that a file was changed. * There is no information in this signal if the file was ever contained in the cache. * The signal may be emitted under CacheLock. Strongly consider a queued connection. */ void fileChanged(const QString& filePath); /** * This signal is emitted when the cache is notified that a file was changed, * and the given cache key was removed from the cache. * The signal may be emitted under CacheLock. Strongly consider a queued connection. */ void fileChanged(const QString& filePath, const QString& cacheKey); private Q_SLOTS: void iccSettingsChanged(const ICCSettingsContainer& current, const ICCSettingsContainer& previous); private: LoadingCache(); friend class LoadingCacheFileWatch; friend class CacheLock; private: static LoadingCache* m_instance; class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_LOADING_CACHE_H diff --git a/core/libs/threadimageio/fileio/loadsavetask.cpp b/core/libs/threadimageio/fileio/loadsavetask.cpp index bdfef3e9be..8794ed849d 100644 --- a/core/libs/threadimageio/fileio/loadsavetask.cpp +++ b/core/libs/threadimageio/fileio/loadsavetask.cpp @@ -1,490 +1,490 @@ /* ============================================================ * * 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-2013 by Marcel Wiesweg * Copyright (C) 2005-2019 by Gilles Caulier * * 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" // Qt includes // Local includes #include "digikam_debug.h" #include "iccmanager.h" #include "icctransform.h" #include "loadsavethread.h" #include "managedloadsavethread.h" #include "sharedloadsavethread.h" #include "loadingcache.h" namespace Digikam { void LoadingTask::execute() { if (m_loadingTaskStatus == LoadingTaskStatusStopping) { return; } DImg img(m_loadingDescription.filePath, this, m_loadingDescription.rawDecodingSettings); m_thread->taskHasFinished(); m_thread->imageLoaded(m_loadingDescription, img); } LoadingTask::TaskType LoadingTask::type() { return TaskTypeLoading; } void LoadingTask::progressInfo(DImg* const img, float progress) { Q_UNUSED(img); if (m_loadingTaskStatus == LoadingTaskStatusLoading) { if (m_thread && m_thread->querySendNotifyEvent()) { m_thread->loadingProgress(m_loadingDescription, progress); } } } bool LoadingTask::continueQuery(DImg* const img) { Q_UNUSED(img); return m_loadingTaskStatus != LoadingTaskStatusStopping; } void LoadingTask::setStatus(LoadingTaskStatus status) { m_loadingTaskStatus = status; } //--------------------------------------------------------------------------------------------------- SharedLoadingTask::SharedLoadingTask(LoadSaveThread* const thread, const LoadingDescription& description, LoadSaveThread::AccessMode mode, LoadingTaskStatus loadingTaskStatus) : LoadingTask(thread, description, loadingTaskStatus), m_completed(false), m_accessMode(mode), m_usedProcess(0), m_resultLoadingDescription(description) { if (m_accessMode == LoadSaveThread::AccessModeRead && needsPostProcessing()) { m_accessMode = LoadSaveThread::AccessModeReadWrite; } } void SharedLoadingTask::execute() { if (m_loadingTaskStatus == LoadingTaskStatusStopping) { return; } // send StartedLoadingEvent from each single Task, not via LoadingProcess list m_thread->imageStartedLoading(m_loadingDescription); LoadingCache* cache = LoadingCache::cache(); { LoadingCache::CacheLock lock(cache); // find possible cached images DImg* cachedImg = 0; QStringList lookupKeys = m_loadingDescription.lookupCacheKeys(); foreach (const QString& key, lookupKeys) { if ((cachedImg = cache->retrieveImage(key))) { if (m_loadingDescription.needCheckRawDecoding()) { if (cachedImg->rawDecodingSettings() == m_loadingDescription.rawDecodingSettings) { break; } else { cachedImg = 0; } } else { break; } } } if (cachedImg) { // image is found in image cache, loading is successful m_img = *cachedImg; } else { // find possible running loading process m_usedProcess = 0; for (QStringList::const_iterator it = lookupKeys.constBegin() ; it != lookupKeys.constEnd() ; ++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_loadingTaskStatus != LoadingTaskStatusStopping && m_usedProcess && !m_usedProcess->completed()) { lock.timedWait(); } // remove listener from process if (m_usedProcess) { 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; // m_img is now set to the result } 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); } } } if (m_img.isNull()) { // load image m_img = DImg(m_loadingDescription.filePath, this, m_loadingDescription.rawDecodingSettings); LoadingCache::CacheLock lock(cache); // put (valid) image into cache of loaded images if (!m_img.isNull()) { cache->putImage(m_loadingDescription.cacheKey(), new DImg(m_img.copy()), m_loadingDescription.filePath); } // remove this from the list of loading processes in cache cache->removeLoadingProcess(this); //qCDebug(DIGIKAM_GENERAL_LOG) << "SharedLoadingTask " << this << ": image loaded, " << img.isNull(); // indicate that loading has finished so that listeners can stop waiting m_completed = true; // dispatch image to all listeners, including this for (int i = 0 ; i < m_listeners.count() ; ++i) { LoadingProcessListener* const l = m_listeners[i]; if (l->accessMode() == LoadSaveThread::AccessModeReadWrite) { // If a listener requested ReadWrite access, it gets a deep copy. // DImg is explicitly shared. l->setResult(m_loadingDescription, m_img.copy()); } else { l->setResult(m_loadingDescription, m_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; } // following the golden rule to avoid deadlocks, do this when CacheLock is not held if (!m_img.isNull() && continueQuery(&m_img)) { if (accessMode() == LoadSaveThread::AccessModeReadWrite) { m_img.detach(); } postProcess(); } else if (continueQuery(&m_img)) { qCWarning(DIGIKAM_GENERAL_LOG) << "Cannot load image for" << m_loadingDescription.filePath; } else { m_img = DImg(); } m_thread->taskHasFinished(); m_thread->imageLoaded(m_loadingDescription, m_img); } void SharedLoadingTask::setResult(const LoadingDescription& loadingDescription, const DImg& img) { // this is called from another process's execute while this task is waiting on m_usedProcess. // Note that loadingDescription need not equal m_loadingDescription (may be superior) m_resultLoadingDescription = loadingDescription; // these are taken from our own description m_resultLoadingDescription.postProcessingParameters = m_loadingDescription.postProcessingParameters; m_img = img; } bool SharedLoadingTask::needsPostProcessing() const { return m_loadingDescription.postProcessingParameters.needsProcessing(); } void SharedLoadingTask::postProcess() { // to receive progress info again. Should be safe now, we are alone. addListener(this); // ---- Color management ---- // switch (m_loadingDescription.postProcessingParameters.colorManagement) { case LoadingDescription::NoColorConversion: break; case LoadingDescription::ApplyTransform: { IccTransform trans = m_loadingDescription.postProcessingParameters.transform(); trans.apply(m_img, this); m_img.setIccProfile(trans.outputProfile()); break; } case LoadingDescription::ConvertForEditor: { IccManager manager(m_img); manager.transformDefault(); break; } case LoadingDescription::ConvertToSRGB: { IccManager manager(m_img); manager.transformToSRGB(); break; } case LoadingDescription::ConvertForDisplay: { IccManager manager(m_img); manager.transformForDisplay(m_loadingDescription.postProcessingParameters.profile()); break; } case LoadingDescription::ConvertForOutput: { IccManager manager(m_img); manager.transformForOutput(m_loadingDescription.postProcessingParameters.profile()); break; } } removeListener(this); } void SharedLoadingTask::progressInfo(DImg* const img, float progress) { Q_UNUSED(img); if (m_loadingTaskStatus == LoadingTaskStatusLoading) { LoadingCache* cache = LoadingCache::cache(); LoadingCache::CacheLock lock(cache); for (int i = 0 ; i < m_listeners.size() ; ++i) { LoadingProcessListener* const l = m_listeners[i]; LoadSaveNotifier* const notifier = l->loadSaveNotifier(); if (notifier && l->querySendNotifyEvent()) { notifier->loadingProgress(m_loadingDescription, progress); } } } } bool SharedLoadingTask::continueQuery(DImg* const img) { Q_UNUSED(img); // 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); // set m_usedProcess to 0, signalling that we have detached already m_usedProcess = 0; // wake all listeners - particularly this - from waiting on cache condvar lock.wakeAll(); } } } -bool SharedLoadingTask::completed() +bool SharedLoadingTask::completed() const volatile { return m_completed; } QString SharedLoadingTask::filePath() const { return m_loadingDescription.filePath; } QString SharedLoadingTask::cacheKey() const { return m_loadingDescription.cacheKey(); } void SharedLoadingTask::addListener(LoadingProcessListener* const listener) { m_listeners << listener; } void SharedLoadingTask::removeListener(LoadingProcessListener* const listener) { m_listeners.removeAll(listener); } void SharedLoadingTask::notifyNewLoadingProcess(LoadingProcess* const process, const 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 (int i = 0 ; i < m_listeners.size() ; ++i) { m_listeners[i]->loadSaveNotifier()->moreCompleteLoadingAvailable(m_loadingDescription, description); } } } bool SharedLoadingTask::querySendNotifyEvent() const { return m_thread && m_thread->querySendNotifyEvent(); } LoadSaveNotifier* SharedLoadingTask::loadSaveNotifier() const { return m_thread; } LoadSaveThread::AccessMode SharedLoadingTask::accessMode() { return m_accessMode; } //--------------------------------------------------------------------------------------------------- void SavingTask::execute() { m_thread->imageStartedSaving(m_filePath); bool success = m_img.save(m_filePath, m_format, this); m_thread->taskHasFinished(); m_thread->imageSaved(m_filePath, success); } LoadingTask::TaskType SavingTask::type() { return TaskTypeSaving; } void SavingTask::progressInfo(DImg* const img, float progress) { Q_UNUSED(img); if (m_thread->querySendNotifyEvent()) { m_thread->savingProgress(m_filePath, progress); } } bool SavingTask::continueQuery(DImg* const img) { Q_UNUSED(img); return (m_savingTaskStatus != SavingTaskStatusStopping); } void SavingTask::setStatus(SavingTaskStatus status) { m_savingTaskStatus = status; } } //namespace Digikam diff --git a/core/libs/threadimageio/fileio/loadsavetask.h b/core/libs/threadimageio/fileio/loadsavetask.h index ce176b42d7..4fce170619 100644 --- a/core/libs/threadimageio/fileio/loadsavetask.h +++ b/core/libs/threadimageio/fileio/loadsavetask.h @@ -1,236 +1,236 @@ /* ============================================================ * * 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-2013 by Marcel Wiesweg * Copyright (C) 2005-2019 by Gilles Caulier * * 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 DIGIKAM_LOAD_SAVE_TASK_H #define DIGIKAM_LOAD_SAVE_TASK_H // Qt includes #include #include // Local includes #include "dimg.h" #include "dimgloaderobserver.h" #include "loadingdescription.h" #include "loadingcache.h" namespace Digikam { class LoadSaveThread; class LoadSaveTask { public: enum TaskType { TaskTypeLoading, TaskTypeSaving }; public: explicit LoadSaveTask(LoadSaveThread* const thread) : m_thread(thread) { }; virtual ~LoadSaveTask() { }; virtual void execute() = 0; virtual TaskType type() = 0; virtual void progressInfo(DImg* const img, float progress) = 0; virtual bool continueQuery(DImg* const img) = 0; protected: LoadSaveThread* m_thread; }; //--------------------------------------------------------------------------------------------------- class LoadingTask : public LoadSaveTask, public DImgLoaderObserver { public: enum LoadingTaskStatus { LoadingTaskStatusLoading, LoadingTaskStatusPreloading, LoadingTaskStatusStopping }; public: explicit LoadingTask(LoadSaveThread* const thread, const LoadingDescription& description, LoadingTaskStatus loadingTaskStatus = LoadingTaskStatusLoading) : LoadSaveTask(thread), m_loadingDescription(description), m_loadingTaskStatus(loadingTaskStatus) { } LoadingTaskStatus status() const { return m_loadingTaskStatus; } QString filePath() const { return m_loadingDescription.filePath; } const LoadingDescription& loadingDescription() const { return m_loadingDescription; } // LoadSaveTask virtual void execute() Q_DECL_OVERRIDE; virtual TaskType type() Q_DECL_OVERRIDE; // DImgLoaderObserver virtual void progressInfo(DImg* const img, float progress) Q_DECL_OVERRIDE; virtual bool continueQuery(DImg* const img) Q_DECL_OVERRIDE; virtual void setStatus(LoadingTaskStatus status); protected: LoadingDescription m_loadingDescription; LoadingTaskStatus m_loadingTaskStatus; }; //--------------------------------------------------------------------------------------------------- class SharedLoadingTask : public LoadingTask, public LoadingProcess, public LoadingProcessListener { public: explicit SharedLoadingTask(LoadSaveThread* const thread, const LoadingDescription& description, LoadSaveThread::AccessMode mode = LoadSaveThread::AccessModeReadWrite, LoadingTaskStatus loadingTaskStatus = LoadingTaskStatusLoading); virtual void execute() Q_DECL_OVERRIDE; virtual void progressInfo(DImg* const img, float progress) Q_DECL_OVERRIDE; virtual bool continueQuery(DImg* const img) Q_DECL_OVERRIDE; virtual void setStatus(LoadingTaskStatus status); virtual bool needsPostProcessing() const; virtual void postProcess(); // LoadingProcess - virtual bool completed(); + virtual bool completed() const volatile; virtual QString filePath() const; virtual QString cacheKey() const; virtual void addListener(LoadingProcessListener* const listener); virtual void removeListener(LoadingProcessListener* const listener); virtual void notifyNewLoadingProcess(LoadingProcess* const process, const LoadingDescription& description); // LoadingProcessListener virtual bool querySendNotifyEvent() const; virtual void setResult(const LoadingDescription& loadingDescription, const DImg& img); virtual LoadSaveNotifier* loadSaveNotifier() const; virtual LoadSaveThread::AccessMode accessMode(); DImg img() { return m_img; } protected: - bool m_completed; + volatile bool m_completed; LoadSaveThread::AccessMode m_accessMode; LoadingProcess* m_usedProcess; QList m_listeners; DImg m_img; LoadingDescription m_resultLoadingDescription; }; //--------------------------------------------------------------------------------------------------- class SavingTask : public LoadSaveTask, public DImgLoaderObserver { public: enum SavingTaskStatus { SavingTaskStatusSaving, SavingTaskStatusStopping }; public: explicit SavingTask(LoadSaveThread* const thread, const DImg& img, const QString& filePath, const QString& format) : LoadSaveTask(thread), m_filePath(filePath), m_format(format), m_img(img), m_savingTaskStatus(SavingTaskStatusSaving) { }; SavingTaskStatus status() const { return m_savingTaskStatus; } QString filePath() const { return m_filePath; } public: virtual void execute() Q_DECL_OVERRIDE; virtual TaskType type() Q_DECL_OVERRIDE; virtual void progressInfo(DImg* const img, float progress) Q_DECL_OVERRIDE; virtual bool continueQuery(DImg* const img) Q_DECL_OVERRIDE; virtual void setStatus(SavingTaskStatus status); private: QString m_filePath; QString m_format; DImg m_img; SavingTaskStatus m_savingTaskStatus; }; } // namespace Digikam #endif // DIGIKAM_LOAD_SAVE_TASK_H