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