File indexing completed on 2025-03-09 03:52:06

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2007-11-11
0007  * Description : a presentation tool.
0008  *
0009  * SPDX-FileCopyrightText: 2007-2009 by Valerio Fuoglio <valerio dot fuoglio at gmail dot com>
0010  * SPDX-FileCopyrightText: 2009      by Andi Clemens <andi dot clemens at googlemail dot com>
0011  * SPDX-FileCopyrightText: 2012-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #include "presentationloader.h"
0018 
0019 // Qt includes
0020 
0021 #include <QMap>
0022 #include <QPainter>
0023 #include <QThread>
0024 #include <QMutex>
0025 #include <QFileInfo>
0026 
0027 // Local includes
0028 
0029 #include "dimg.h"
0030 #include "iccmanager.h"
0031 #include "iccsettings.h"
0032 #include "digikam_debug.h"
0033 #include "previewloadthread.h"
0034 #include "iccsettingscontainer.h"
0035 #include "presentationcontainer.h"
0036 
0037 using namespace Digikam;
0038 
0039 namespace DigikamGenericPresentationPlugin
0040 {
0041 
0042 typedef QMap<QUrl, QImage> LoadedImages;
0043 
0044 class Q_DECL_HIDDEN LoadThread : public QThread
0045 {
0046     Q_OBJECT
0047 
0048 public:
0049 
0050     LoadThread(LoadedImages* const loadedImages,
0051                QMutex* const imageLock,
0052                QWidget* const display,
0053                const QUrl& path,
0054                int width,
0055                int height)
0056         : m_imageLock   (imageLock),
0057           m_loadedImages(loadedImages),
0058           m_display     (display),
0059           m_path        (path),
0060           m_swidth      (width),
0061           m_sheight     (height)
0062     {
0063     }
0064 
0065     ~LoadThread() override
0066     {
0067     }
0068 
0069 protected:
0070 
0071     void run() override
0072     {
0073         QImage newImage;
0074 
0075         ICCSettingsContainer settings = IccSettings::instance()->settings();
0076 
0077         if (settings.enableCM && settings.useManagedPreviews)
0078         {
0079             IccProfile profile(IccManager::displayProfile(m_display));
0080 
0081             newImage = PreviewLoadThread::loadHighQualitySynchronously(m_path.toLocalFile(),
0082                                                                        PreviewSettings::RawPreviewAutomatic,
0083                                                                        profile).copyQImage();
0084         }
0085         else
0086         {
0087             newImage = PreviewLoadThread::loadHighQualitySynchronously(m_path.toLocalFile()).copyQImage();
0088         }
0089 
0090         m_imageLock->lock();
0091 
0092         if (newImage.isNull())
0093         {
0094             m_loadedImages->insert(m_path, newImage);
0095         }
0096         else
0097         {
0098             m_loadedImages->insert(m_path, newImage.scaled(m_swidth,
0099                                                            m_sheight,
0100                                                            Qt::KeepAspectRatio,
0101                                                            Qt::SmoothTransformation));
0102         }
0103 
0104         m_imageLock->unlock();
0105     }
0106 
0107 private:
0108 
0109     // Disable
0110     explicit LoadThread(QObject*);
0111 
0112 private:
0113 
0114     QMutex*       m_imageLock;
0115     LoadedImages* m_loadedImages;
0116     QWidget*      m_display;
0117     QUrl          m_path;
0118     QString       m_filename;
0119     int           m_swidth;
0120     int           m_sheight;
0121 };
0122 
0123 typedef QMap<QUrl, LoadThread*> LoadingThreads;
0124 
0125 // -----------------------------------------------------------------------------------------
0126 
0127 class Q_DECL_HIDDEN PresentationLoader::Private
0128 {
0129 
0130 public:
0131 
0132     explicit Private()
0133       : sharedData      (nullptr),
0134         loadingThreads  (nullptr),
0135         loadedImages    (nullptr),
0136         imageLock       (nullptr),
0137         threadLock      (nullptr),
0138         cacheSize       (0),
0139         currIndex       (0),
0140         swidth          (0),
0141         sheight         (0)
0142     {
0143     }
0144 
0145     PresentationContainer* sharedData;
0146     LoadingThreads*        loadingThreads;
0147     LoadedImages*          loadedImages;
0148 
0149     QMutex*                imageLock;
0150     QMutex*                threadLock;
0151 
0152     uint                   cacheSize;
0153     int                    currIndex;
0154     int                    swidth;
0155     int                    sheight;
0156 };
0157 
0158 PresentationLoader::PresentationLoader(PresentationContainer* const sharedData,
0159                                        int width,
0160                                        int height,
0161                                        int beginAtIndex)
0162     : d(new Private)
0163 {
0164     d->sharedData     = sharedData;
0165     d->currIndex      = beginAtIndex;
0166     d->cacheSize      = d->sharedData->enableCache ? d->sharedData->cacheSize : 1;
0167     d->swidth         = width;
0168     d->sheight        = height;
0169     d->loadingThreads = new LoadingThreads();
0170     d->loadedImages   = new LoadedImages();
0171     d->imageLock      = new QMutex();
0172     d->threadLock     = new QMutex();
0173 
0174     QUrl filePath;
0175 
0176     for (uint i = 0 ; (i < uint(d->cacheSize / 2)) && (i < uint(d->sharedData->urlList.count())) ; ++i)
0177     {
0178         filePath                    = d->sharedData->urlList[i];
0179         LoadThread* const newThread = new LoadThread(d->loadedImages, d->imageLock, d->sharedData->display,
0180                                                      filePath, d->swidth, d->sheight);
0181         d->threadLock->lock();
0182         d->loadingThreads->insert(filePath, newThread);
0183         newThread->start();
0184         d->threadLock->unlock();
0185     }
0186 
0187     for (uint i = 0 ; (i < (((d->cacheSize % 2) == 0) ? (d->cacheSize % 2) : uint(d->cacheSize / 2) + 1)) ; ++i)
0188     {
0189         int toLoad                  = (d->currIndex - i) % d->sharedData->urlList.count();
0190         filePath                    = d->sharedData->urlList[toLoad];
0191         LoadThread* const newThread = new LoadThread(d->loadedImages, d->imageLock, d->sharedData->display,
0192                                                      filePath, d->swidth, d->sheight);
0193         d->threadLock->lock();
0194         d->loadingThreads->insert(filePath, newThread);
0195         newThread->start();
0196         d->threadLock->unlock();
0197     }
0198 }
0199 
0200 PresentationLoader::~PresentationLoader()
0201 {
0202     d->threadLock->lock();
0203     LoadingThreads::Iterator it;
0204 
0205     for (it = d->loadingThreads->begin() ; it != d->loadingThreads->end() ; ++it)
0206     {
0207         // better check for a valid pointer here
0208 
0209         if (it.value())
0210         {
0211             it.value()->wait();
0212         }
0213 
0214         delete it.value();
0215     }
0216 
0217     d->loadingThreads->clear();
0218 
0219     d->threadLock->unlock();
0220 
0221     delete d->loadedImages;
0222     delete d->loadingThreads;
0223     delete d->imageLock;
0224     delete d->threadLock;
0225     delete d;
0226 }
0227 
0228 void PresentationLoader::next()
0229 {
0230     int victim   = (d->currIndex - (d->cacheSize % 2 == 0 ? (d->cacheSize / 2) - 1
0231                                                           :  int(d->cacheSize / 2))) % d->sharedData->urlList.count();
0232 
0233     int newBorn  = (d->currIndex + int(d->cacheSize / 2) + 1) % d->sharedData->urlList.count();
0234     d->currIndex = (d->currIndex + 1) % d->sharedData->urlList.count();
0235 
0236     if (victim == newBorn)
0237     {
0238         return;
0239     }
0240 
0241     d->threadLock->lock();
0242 
0243     LoadThread* const oldThread = d->loadingThreads->value(d->sharedData->urlList[victim]);
0244 
0245     if (oldThread)
0246     {
0247         oldThread->wait();
0248     }
0249 
0250     delete oldThread;
0251 
0252     d->loadingThreads->remove(d->sharedData->urlList[victim]);
0253     d->imageLock->lock();
0254     d->loadedImages->remove(d->sharedData->urlList[victim]);
0255     d->imageLock->unlock();
0256     d->threadLock->unlock();
0257 
0258     QUrl filePath               = d->sharedData->urlList[newBorn];
0259     LoadThread* const newThread = new LoadThread(d->loadedImages, d->imageLock, d->sharedData->display,
0260                                                  filePath, d->swidth, d->sheight);
0261 
0262     d->threadLock->lock();
0263 
0264     d->loadingThreads->insert(filePath, newThread);
0265     newThread->start();
0266 
0267     d->threadLock->unlock();
0268 }
0269 
0270 void PresentationLoader::prev()
0271 {
0272     int victim   = (d->currIndex + int(d->currIndex / 2)) % d->sharedData->urlList.count();
0273     int newBorn  = (d->currIndex - ((d->cacheSize & 2) == 0 ? (d->cacheSize / 2)
0274                                                             : int(d->cacheSize / 2) + 1)) % d->sharedData->urlList.count();
0275 
0276     d->currIndex = d->currIndex > 0 ? d->currIndex - 1 : d->sharedData->urlList.count() - 1;
0277 
0278     if (victim == newBorn)
0279     {
0280         return;
0281     }
0282 
0283     d->threadLock->lock();
0284     d->imageLock->lock();
0285 
0286     LoadThread* const oldThread = d->loadingThreads->value(d->sharedData->urlList[victim]);
0287 
0288     if (oldThread)
0289     {
0290         oldThread->wait();
0291     }
0292 
0293     delete oldThread;
0294 
0295     d->loadingThreads->remove(d->sharedData->urlList[victim]);
0296     d->loadedImages->remove(d->sharedData->urlList[victim]);
0297 
0298     d->imageLock->unlock();
0299     d->threadLock->unlock();
0300 
0301     QUrl filePath               = d->sharedData->urlList[newBorn];
0302     LoadThread* const newThread = new LoadThread(d->loadedImages, d->imageLock, d->sharedData->display,
0303                                                  filePath, d->swidth, d->sheight);
0304 
0305     d->threadLock->lock();
0306 
0307     d->loadingThreads->insert(filePath, newThread);
0308     newThread->start();
0309 
0310     d->threadLock->unlock();
0311 }
0312 
0313 QImage PresentationLoader::getCurrent() const
0314 {
0315     checkIsIn(d->currIndex);
0316     d->imageLock->lock();
0317     QImage returned = (*d->loadedImages)[d->sharedData->urlList[d->currIndex]];
0318     d->imageLock->unlock();
0319 
0320     return returned;
0321 }
0322 
0323 QString PresentationLoader::currFileName() const
0324 {
0325     return d->sharedData->urlList[d->currIndex].fileName();
0326 }
0327 
0328 QUrl PresentationLoader::currPath() const
0329 {
0330     return d->sharedData->urlList[d->currIndex];
0331 }
0332 
0333 void PresentationLoader::checkIsIn(int index) const
0334 {
0335     d->threadLock->lock();
0336 
0337     if (d->loadingThreads->contains(d->sharedData->urlList[index]))
0338     {
0339         if ((*d->loadingThreads)[d->sharedData->urlList[index]]->isRunning())
0340         {
0341             (*d->loadingThreads)[d->sharedData->urlList[index]]->wait();
0342         }
0343 
0344         d->threadLock->unlock();
0345     }
0346     else
0347     {
0348         QUrl filePath               = d->sharedData->urlList[index];
0349         LoadThread* const newThread = new LoadThread(d->loadedImages, d->imageLock, d->sharedData->display,
0350                                                      filePath, d->swidth, d->sheight);
0351 
0352         d->loadingThreads->insert(d->sharedData->urlList[index], newThread);
0353         newThread->start();
0354         (*d->loadingThreads)[d->sharedData->urlList[index]]->wait();
0355         d->threadLock->unlock();
0356     }
0357 }
0358 
0359 } // namespace DigikamGenericPresentationPlugin
0360 
0361 #include "presentationloader.moc"