File indexing completed on 2025-04-27 03:58:08
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2005-12-17 0007 * Description : image file IO threaded interface. 0008 * 0009 * SPDX-FileCopyrightText: 2005-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0010 * SPDX-FileCopyrightText: 2005-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 "loadsavethread.h" 0017 0018 // Local includes 0019 0020 #include "metaengine_rotation.h" 0021 #include "dmetadata.h" 0022 #include "managedloadsavethread.h" 0023 #include "sharedloadsavethread.h" 0024 #include "loadsavetask.h" 0025 0026 namespace Digikam 0027 { 0028 0029 LoadSaveNotifier::LoadSaveNotifier() 0030 { 0031 } 0032 0033 LoadSaveNotifier::~LoadSaveNotifier() 0034 { 0035 } 0036 0037 // -------------------------------------------------------------------------------- 0038 0039 LoadSaveFileInfoProvider::LoadSaveFileInfoProvider() 0040 { 0041 } 0042 0043 LoadSaveFileInfoProvider::~LoadSaveFileInfoProvider() 0044 { 0045 } 0046 0047 // -------------------------------------------------------------------------------- 0048 0049 class Q_DECL_HIDDEN LoadSaveThread::Private 0050 { 0051 public: 0052 0053 explicit Private() 0054 : running (true), 0055 blockNotification(false), 0056 lastTask (nullptr) 0057 { 0058 } 0059 0060 bool running; 0061 bool blockNotification; 0062 0063 QTime notificationTime; 0064 0065 LoadSaveTask* lastTask; 0066 0067 static LoadSaveFileInfoProvider* infoProvider; 0068 }; 0069 0070 LoadSaveFileInfoProvider* LoadSaveThread::Private::infoProvider = nullptr; 0071 0072 //--------------------------------------------------------------------------------------------------- 0073 0074 LoadSaveThread::LoadSaveThread(QObject* const parent) 0075 : DynamicThread (parent), 0076 m_currentTask (nullptr), 0077 m_notificationPolicy(NotificationPolicyTimeLimited), 0078 d (new Private) 0079 { 0080 } 0081 0082 LoadSaveThread::~LoadSaveThread() 0083 { 0084 shutDown(); 0085 delete d; 0086 } 0087 0088 void LoadSaveThread::setInfoProvider(LoadSaveFileInfoProvider* const infoProvider) 0089 { 0090 Private::infoProvider = infoProvider; 0091 } 0092 0093 LoadSaveFileInfoProvider* LoadSaveThread::infoProvider() 0094 { 0095 return Private::infoProvider; 0096 } 0097 0098 void LoadSaveThread::load(const LoadingDescription& description) 0099 { 0100 QMutexLocker lock(threadMutex()); 0101 m_todo << new LoadingTask(this, description); 0102 start(lock); 0103 } 0104 0105 void LoadSaveThread::save(const DImg& image, const QString& filePath, const QString& format) 0106 { 0107 QMutexLocker lock(threadMutex()); 0108 m_todo << new SavingTask(this, image, filePath, format); 0109 start(lock); 0110 } 0111 0112 void LoadSaveThread::run() 0113 { 0114 while (runningFlag()) 0115 { 0116 { 0117 QMutexLocker lock(threadMutex()); 0118 0119 delete d->lastTask; 0120 d->lastTask = nullptr; 0121 delete m_currentTask; 0122 m_currentTask = nullptr; 0123 0124 if (!m_todo.isEmpty()) 0125 { 0126 m_currentTask = m_todo.takeFirst(); 0127 0128 if (m_notificationPolicy == NotificationPolicyTimeLimited) 0129 { 0130 // set timing values so that first event is sent only 0131 // after an initial time span. 0132 0133 d->notificationTime = QTime::currentTime(); 0134 d->blockNotification = true; 0135 } 0136 } 0137 else 0138 { 0139 stop(lock); 0140 } 0141 } 0142 0143 if (m_currentTask) 0144 { 0145 m_currentTask->execute(); 0146 } 0147 } 0148 } 0149 0150 void LoadSaveThread::taskHasFinished() 0151 { 0152 // This function is called by the tasks _before_ they send their _final_ message. 0153 // This is to guarantee the user of the API that at least the final message 0154 // is sent after load() has been called. 0155 // We set m_currentTask to 0 here. If a new task is appended, base classes usually check 0156 // that m_currentTask is not currently loading the same task. 0157 // Now it might happen that m_currentTask has already emitted its final signal, 0158 // but the new task is rejected afterwards when m_currentTask is still the task 0159 // that has actually already finished (execute() in the loop above is of course not under mutex). 0160 // So we set m_currentTask to 0 immediately before the final message is emitted, 0161 // so that anyone who finds this task running as m_current task will get a message. 0162 0163 QMutexLocker lock(threadMutex()); 0164 d->lastTask = m_currentTask; 0165 m_currentTask = nullptr; 0166 } 0167 0168 void LoadSaveThread::imageStartedLoading(const LoadingDescription& loadingDescription) 0169 { 0170 notificationReceived(); 0171 Q_EMIT signalImageStartedLoading(loadingDescription); 0172 } 0173 0174 void LoadSaveThread::loadingProgress(const LoadingDescription& loadingDescription, float progress) 0175 { 0176 notificationReceived(); 0177 Q_EMIT signalLoadingProgress(loadingDescription, progress); 0178 } 0179 0180 void LoadSaveThread::imageLoaded(const LoadingDescription& loadingDescription, const DImg& img) 0181 { 0182 notificationReceived(); 0183 Q_EMIT signalImageLoaded(loadingDescription, img); 0184 } 0185 0186 void LoadSaveThread::moreCompleteLoadingAvailable(const LoadingDescription& oldLoadingDescription, 0187 const LoadingDescription& newLoadingDescription) 0188 { 0189 notificationReceived(); 0190 Q_EMIT signalMoreCompleteLoadingAvailable(oldLoadingDescription, newLoadingDescription); 0191 } 0192 0193 void LoadSaveThread::imageStartedSaving(const QString& filePath) 0194 { 0195 notificationReceived(); 0196 Q_EMIT signalImageStartedSaving(filePath); 0197 } 0198 0199 void LoadSaveThread::savingProgress(const QString& filePath, float progress) 0200 { 0201 notificationReceived(); 0202 Q_EMIT signalSavingProgress(filePath, progress); 0203 } 0204 0205 void LoadSaveThread::imageSaved(const QString& filePath, bool success) 0206 { 0207 notificationReceived(); 0208 Q_EMIT signalImageSaved(filePath, success); 0209 } 0210 0211 void LoadSaveThread::thumbnailLoaded(const LoadingDescription& loadingDescription, const QImage& img) 0212 { 0213 notificationReceived(); 0214 Q_EMIT signalThumbnailLoaded(loadingDescription, img); 0215 } 0216 0217 void LoadSaveThread::notificationReceived() 0218 { 0219 switch (m_notificationPolicy) 0220 { 0221 case NotificationPolicyDirect: 0222 { 0223 d->blockNotification = false; 0224 break; 0225 } 0226 0227 case NotificationPolicyTimeLimited: 0228 { 0229 break; 0230 } 0231 } 0232 } 0233 0234 void LoadSaveThread::setNotificationPolicy(NotificationPolicy notificationPolicy) 0235 { 0236 m_notificationPolicy = notificationPolicy; 0237 d->blockNotification = false; 0238 } 0239 0240 bool LoadSaveThread::querySendNotifyEvent() const 0241 { 0242 // This function is called from the thread to ask for permission to send a notify event. 0243 0244 switch (m_notificationPolicy) 0245 { 0246 case NotificationPolicyDirect: 0247 { 0248 // Note that m_blockNotification is not protected by a mutex. However, if there is a 0249 // race condition, the worst case is that one event is not sent, which is no problem. 0250 0251 if (d->blockNotification) 0252 { 0253 return false; 0254 } 0255 else 0256 { 0257 d->blockNotification = true; 0258 0259 return true; 0260 } 0261 0262 break; 0263 } 0264 0265 case NotificationPolicyTimeLimited: 0266 { 0267 // Current default time value: 100 millisecs. 0268 0269 if (d->blockNotification) 0270 { 0271 d->blockNotification = d->notificationTime.msecsTo(QTime::currentTime()) < 100; 0272 } 0273 0274 if (d->blockNotification) 0275 { 0276 return false; 0277 } 0278 else 0279 { 0280 d->notificationTime = QTime::currentTime(); 0281 d->blockNotification = true; 0282 0283 return true; 0284 } 0285 0286 break; 0287 } 0288 } 0289 0290 return false; 0291 } 0292 0293 int LoadSaveThread::exifOrientation(const QString& filePath, 0294 const DMetadata& metadata, 0295 bool isRaw, 0296 bool fromRawEmbeddedPreview) 0297 { 0298 int dbOrientation = MetaEngine::ORIENTATION_UNSPECIFIED; 0299 0300 if (infoProvider()) 0301 { 0302 dbOrientation = infoProvider()->orientationHint(filePath); 0303 } 0304 0305 int exifOrientation = metadata.getItemOrientation(); 0306 0307 // Raw files are already rotated properly by Raw engine. Only perform auto-rotation with JPEG/PNG/TIFF file. 0308 // We don't have a feedback from Raw engine about auto-rotated RAW file during decoding. 0309 0310 if (isRaw && !fromRawEmbeddedPreview) 0311 { 0312 // Did the user apply any additional rotation over the metadata flag? 0313 0314 if (dbOrientation == MetaEngine::ORIENTATION_UNSPECIFIED || dbOrientation == exifOrientation) 0315 { 0316 return MetaEngine::ORIENTATION_NORMAL; 0317 } 0318 0319 // Assume A is the orientation as from metadata, B is an additional operation applied by the user, 0320 // C is the current orientation in the database. 0321 // A*B = C and B = A_inv * C 0322 0323 QTransform A = MetaEngineRotation::toTransform((MetaEngine::ImageOrientation)exifOrientation); 0324 QTransform C = MetaEngineRotation::toTransform((MetaEngine::ImageOrientation)dbOrientation); 0325 QTransform A_inv = A.inverted(); 0326 QTransform B = A_inv * C; 0327 MetaEngineRotation m(B.m11(), B.m12(), B.m21(), B.m22()); 0328 0329 return m.exifOrientation(); 0330 } 0331 0332 if (dbOrientation != MetaEngine::ORIENTATION_UNSPECIFIED) 0333 { 0334 return dbOrientation; 0335 } 0336 0337 return exifOrientation; 0338 } 0339 0340 } // namespace Digikam 0341 0342 #include "moc_loadsavethread.cpp"