File indexing completed on 2025-04-27 03:58:10
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2007-06-05 0007 * Description : Multithreaded loader for thumbnails 0008 * 0009 * SPDX-FileCopyrightText: 2006-2008 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0010 * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0011 * 0012 * SPDX-License-Identifier: GPL-2.0-or-later 0013 * 0014 * ============================================================ */ 0015 0016 #include "thumbnailtask.h" 0017 0018 // C++ includes 0019 0020 #include <cmath> 0021 0022 // Qt includes 0023 0024 #include <QApplication> 0025 #include <QVariant> 0026 0027 // Local includes 0028 0029 #include "drawdecoder.h" 0030 #include "digikam_debug.h" 0031 #include "dmetadata.h" 0032 #include "iccmanager.h" 0033 #include "jpegutils.h" 0034 #include "metaenginesettings.h" 0035 #include "thumbnailloadthread.h" 0036 #include "thumbnailcreator.h" 0037 0038 namespace Digikam 0039 { 0040 0041 ThumbnailLoadingTask::ThumbnailLoadingTask(LoadSaveThread* const thread, const LoadingDescription& description) 0042 : SharedLoadingTask(thread, 0043 description, 0044 LoadSaveThread::AccessModeRead, 0045 LoadingTaskStatusLoading) 0046 { 0047 // Thread must be a ThumbnailLoadThread, crashes otherwise. 0048 // Not a clean but pragmatic solution. 0049 0050 ThumbnailLoadThread* const thumbThread = static_cast<ThumbnailLoadThread*>(thread); 0051 m_creator = thumbThread->thumbnailCreator(); 0052 } 0053 0054 void ThumbnailLoadingTask::execute() 0055 { 0056 if (m_loadingTaskStatus == LoadingTaskStatusStopping) 0057 { 0058 m_thread->taskHasFinished(); 0059 0060 return; 0061 } 0062 0063 if (m_loadingDescription.previewParameters.onlyPregenerate()) 0064 { 0065 setupCreator(); 0066 0067 switch (m_loadingDescription.previewParameters.type) 0068 { 0069 case LoadingDescription::PreviewParameters::Thumbnail: 0070 m_creator->pregenerate(m_loadingDescription.thumbnailIdentifier()); 0071 break; 0072 0073 case LoadingDescription::PreviewParameters::DetailThumbnail: 0074 m_creator->pregenerateDetail(m_loadingDescription.thumbnailIdentifier(), 0075 m_loadingDescription.previewParameters.extraParameter.toRect()); 0076 break; 0077 0078 default: 0079 break; 0080 } 0081 0082 m_thread->taskHasFinished(); 0083 0084 // do not Q_EMIT any signal 0085 0086 return; 0087 } 0088 0089 LoadingCache* const cache = LoadingCache::cache(); 0090 { 0091 LoadingCache::CacheLock lock(cache); 0092 0093 // find possible cached images 0094 0095 const QImage* const cachedImage = cache->retrieveThumbnail(m_loadingDescription.cacheKey()); 0096 0097 if (cachedImage) 0098 { 0099 m_qimage = *cachedImage; 0100 } 0101 0102 if (m_qimage.isNull()) 0103 { 0104 // find possible running loading process 0105 // do not wait on other loading processes? 0106 0107 LoadingProcess* const usedProcess = cache->retrieveLoadingProcess(m_loadingDescription.cacheKey()); 0108 0109 if (usedProcess) 0110 { 0111 // Other process is right now loading this image. 0112 // Add this task to the list of listeners and 0113 // attach this thread to the other thread, wait until loading 0114 // has finished. 0115 0116 usedProcess->addListener(this); 0117 0118 // break loop when either the loading has completed, or this task is being stopped 0119 0120 // cppcheck-suppress knownConditionTrueFalse 0121 while ((m_loadingTaskStatus != LoadingTaskStatusStopping) && !usedProcess->completed()) 0122 { 0123 lock.timedWait(); 0124 } 0125 0126 // remove listener from process 0127 0128 usedProcess->removeListener(this); 0129 0130 // wake up the process which is waiting until all listeners have removed themselves 0131 0132 lock.wakeAll(); 0133 } 0134 } 0135 } 0136 0137 if (continueQuery() && m_qimage.isNull()) 0138 { 0139 { 0140 LoadingCache::CacheLock lock(cache); 0141 0142 // Neither in cache, nor currently loading in different thread. 0143 // Load it here and now, add this LoadingProcess to cache list. 0144 0145 cache->addLoadingProcess(this); 0146 0147 // Notify other processes that we are now loading this image. 0148 // They might be interested - see notifyNewLoadingProcess below 0149 0150 cache->notifyNewLoadingProcess(this, m_loadingDescription); 0151 } 0152 0153 // Load or create thumbnail 0154 0155 setupCreator(); 0156 0157 switch (m_loadingDescription.previewParameters.type) 0158 { 0159 case LoadingDescription::PreviewParameters::Thumbnail: 0160 m_qimage = m_creator->load(m_loadingDescription.thumbnailIdentifier(), 0161 m_loadingDescription.previewParameters.onlyFromStorage()); 0162 break; 0163 0164 case LoadingDescription::PreviewParameters::DetailThumbnail: 0165 m_qimage = m_creator->loadDetail(m_loadingDescription.thumbnailIdentifier(), 0166 m_loadingDescription.previewParameters.extraParameter.toRect(), 0167 m_loadingDescription.previewParameters.onlyFromStorage()); 0168 break; 0169 0170 default: 0171 break; 0172 } 0173 0174 if (continueQuery() && !m_qimage.isNull()) 0175 { 0176 postProcess(); 0177 } 0178 0179 { 0180 LoadingCache::CacheLock lock(cache); 0181 0182 // remove this from the list of loading processes in cache 0183 0184 cache->removeLoadingProcess(this); 0185 0186 // put valid image into cache of loaded images 0187 0188 if (continueQuery() && !m_qimage.isNull()) 0189 { 0190 cache->putThumbnail(m_loadingDescription.cacheKey(), m_qimage, 0191 m_loadingDescription.filePath); 0192 0193 // dispatch image to all listeners 0194 0195 for (int i = 0 ; i < m_listeners.count() ; ++i) 0196 { 0197 ThumbnailLoadingTask* const task = dynamic_cast<ThumbnailLoadingTask*>(m_listeners.at(i)); 0198 0199 if (task) 0200 { 0201 task->setThumbResult(m_loadingDescription, m_qimage); 0202 } 0203 } 0204 } 0205 0206 // indicate that loading has finished so that listeners can stop waiting 0207 0208 m_completed = true; 0209 0210 // wake all listeners waiting on cache condVar, so that they remove themselves 0211 0212 lock.wakeAll(); 0213 0214 // wait until all listeners have removed themselves 0215 0216 while (m_listeners.count() != 0) 0217 { 0218 lock.timedWait(); 0219 } 0220 } 0221 } 0222 0223 if (!continueQuery()) 0224 { 0225 m_qimage = QImage(); 0226 } 0227 0228 m_thread->taskHasFinished(); 0229 m_thread->thumbnailLoaded(m_loadingDescription, m_qimage); 0230 } 0231 0232 void ThumbnailLoadingTask::setupCreator() 0233 { 0234 m_creator->setThumbnailSize(m_loadingDescription.previewParameters.size); 0235 m_creator->setExifRotate(MetaEngineSettings::instance()->settings().exifRotate); 0236 m_creator->setLoadingProperties(this, m_loadingDescription.rawDecodingSettings); 0237 } 0238 0239 void ThumbnailLoadingTask::setThumbResult(const LoadingDescription& loadingDescription, const QImage& qimage) 0240 { 0241 // This is called from another process's execute while this task is waiting on usedProcess. 0242 // Note that loadingDescription need not equal m_loadingDescription (may be superior) 0243 0244 LoadingDescription tempDescription = loadingDescription; 0245 0246 // these are taken from our own description 0247 0248 tempDescription.postProcessingParameters = m_loadingDescription.postProcessingParameters; 0249 m_loadingDescription = tempDescription; 0250 m_qimage = qimage; 0251 } 0252 0253 void ThumbnailLoadingTask::postProcess() 0254 { 0255 m_loadingDescription.postProcessingParameters.profile().description(); 0256 0257 switch (m_loadingDescription.postProcessingParameters.colorManagement) 0258 { 0259 case LoadingDescription::NoColorConversion: 0260 { 0261 break; 0262 } 0263 0264 case LoadingDescription::ConvertToSRGB: 0265 { 0266 // Thumbnails are stored in sRGB 0267 0268 break; 0269 } 0270 0271 case LoadingDescription::ConvertForDisplay: 0272 { 0273 IccManager::transformForDisplay(m_qimage, m_loadingDescription.postProcessingParameters.profile()); 0274 break; 0275 } 0276 0277 default: 0278 { 0279 qCDebug(DIGIKAM_GENERAL_LOG) << "Unsupported postprocessing parameter for thumbnail loading:" 0280 << m_loadingDescription.postProcessingParameters.colorManagement; 0281 break; 0282 } 0283 } 0284 } 0285 0286 } // namespace Digikam