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"