diff --git a/core/libs/threadimageio/loadsavetask.cpp b/core/libs/threadimageio/loadsavetask.cpp index 91ce8da157..81019adcdf 100644 --- a/core/libs/threadimageio/loadsavetask.cpp +++ b/core/libs/threadimageio/loadsavetask.cpp @@ -1,480 +1,480 @@ /* ============================================================ * * 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-2018 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(const DImg* const, float progress) { if (m_loadingTaskStatus == LoadingTaskStatusLoading) { if (m_thread->querySendNotifyEvent()) { m_thread->loadingProgress(m_loadingDescription, progress); } } } bool LoadingTask::continueQuery(const DImg* const) { 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 ((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; if (accessMode() == LoadSaveThread::AccessModeReadWrite) { m_img = m_img.copy(); } // continues after else clause... } else { // find possible running loading process m_usedProcess = 0; - for ( QStringList::const_iterator it = lookupKeys.constBegin(); it != lookupKeys.constEnd(); ++it ) + for (QStringList::const_iterator it = lookupKeys.constBegin() ; it != lookupKeys.constEnd() ; ++it) { - if ( (m_usedProcess = cache->retrieveLoadingProcess(*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() ) + 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; //qCDebug(DIGIKAM_GENERAL_LOG) << "SharedLoadingTask " << this << ": waited"; // 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()) { // following the golden rule to avoid deadlocks, do this when CacheLock is not held postProcess(); m_thread->taskHasFinished(); m_thread->imageLoaded(m_resultLoadingDescription, m_img); return; } // load image m_img = DImg(m_loadingDescription.filePath, this, m_loadingDescription.rawDecodingSettings); // bool isCached = false; { LoadingCache::CacheLock lock(cache); // put (valid) image into cache of loaded images if (!m_img.isNull()) { // isCached = cache->putImage(m_loadingDescription.cacheKey(), new DImg(m_img), m_loadingDescription.filePath); } // remove this from the list of loading processes in cache cache->removeLoadingProcess(this); } { LoadingCache::CacheLock lock(cache); //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; iaccessMode() == LoadSaveThread::AccessModeReadWrite) { // If a listener requested ReadWrite access, it gets a deep copy. // DImg is explicitly shared. DImg copy = m_img.copy(); l->setResult(m_loadingDescription, 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; } // again: following the golden rule to avoid deadlocks, do this when CacheLock is not held postProcess(); 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(const DImg* const, float progress) { if (m_loadingTaskStatus == LoadingTaskStatusLoading) { LoadingCache* cache = LoadingCache::cache(); LoadingCache::CacheLock lock(cache); - for (int i=0; iloadSaveNotifier(); + if (notifier && l->querySendNotifyEvent()) { notifier->loadingProgress(m_loadingDescription, progress); } } } } bool SharedLoadingTask::continueQuery(const DImg* const) { // 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() { return m_completed; } QString SharedLoadingTask::filePath() { return m_loadingDescription.filePath; } QString SharedLoadingTask::cacheKey() { return m_loadingDescription.cacheKey(); } void SharedLoadingTask::addListener(LoadingProcessListener* listener) { m_listeners << listener; } void SharedLoadingTask::removeListener(LoadingProcessListener* listener) { m_listeners.removeAll(listener); } void SharedLoadingTask::notifyNewLoadingProcess(LoadingProcess* 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() - ) + !description.isReducedVersion()) { - for (int i=0; iloadSaveNotifier()->moreCompleteLoadingAvailable(m_loadingDescription, description); } } } bool SharedLoadingTask::querySendNotifyEvent() { return m_thread && m_thread->querySendNotifyEvent(); } LoadSaveNotifier* SharedLoadingTask::loadSaveNotifier() { 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(const DImg* const, float progress) { if (m_thread->querySendNotifyEvent()) { m_thread->savingProgress(m_filePath, progress); } } bool SavingTask::continueQuery(const DImg* const) { return (m_savingTaskStatus != SavingTaskStatusStopping); } void SavingTask::setStatus(SavingTaskStatus status) { m_savingTaskStatus = status; } } //namespace Digikam