File indexing completed on 2025-04-27 03:58:09
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2007-06-05 0007 * Description : Thumbnail loading 0008 * 0009 * SPDX-FileCopyrightText: 2006-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 * SPDX-FileCopyrightText: 2015 by Mohamed_Anwer <m_dot_anwer at gmx dot com> 0012 * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * 0015 * ============================================================ */ 0016 0017 #include "thumbnailloadthread_p.h" 0018 0019 namespace Digikam 0020 { 0021 0022 Q_GLOBAL_STATIC(ThumbnailLoadThreadStaticPriv, static_d) 0023 Q_GLOBAL_STATIC(ThumbnailLoadThread, defaultObject) 0024 Q_GLOBAL_STATIC(ThumbnailLoadThread, defaultIconViewObject) 0025 0026 // --- Creating loading descriptions --- 0027 0028 LoadingDescription ThumbnailLoadThread::Private::createLoadingDescription(const ThumbnailIdentifier& identifier, 0029 int size, 0030 bool setLastDescription) 0031 { 0032 size = thumbnailSizeForPixmapSize(size); 0033 0034 LoadingDescription description(identifier.filePath, PreviewSettings(), size, 0035 LoadingDescription::NoColorConversion, 0036 LoadingDescription::PreviewParameters::Thumbnail); 0037 description.previewParameters.storageReference = identifier.id; 0038 0039 if (IccSettings::instance()->useManagedPreviews()) 0040 { 0041 description.postProcessingParameters.colorManagement = LoadingDescription::ConvertForDisplay; 0042 description.postProcessingParameters.setProfile(static_d->profile); 0043 } 0044 0045 if (setLastDescription) 0046 { 0047 lastDescriptions.clear(); 0048 lastDescriptions << description; 0049 } 0050 0051 return description; 0052 } 0053 0054 LoadingDescription ThumbnailLoadThread::Private::createLoadingDescription(const ThumbnailIdentifier& identifier, 0055 int size, 0056 const QRect& detailRect, 0057 bool setLastDescription) 0058 { 0059 size = thumbnailSizeForPixmapSize(size); 0060 0061 LoadingDescription description(identifier.filePath, PreviewSettings(), size, 0062 LoadingDescription::NoColorConversion, 0063 LoadingDescription::PreviewParameters::DetailThumbnail); 0064 0065 description.previewParameters.storageReference = identifier.id; 0066 description.previewParameters.extraParameter = detailRect; 0067 0068 if (IccSettings::instance()->useManagedPreviews()) 0069 { 0070 description.postProcessingParameters.colorManagement = LoadingDescription::ConvertForDisplay; 0071 description.postProcessingParameters.setProfile(static_d->profile); 0072 } 0073 0074 if (setLastDescription) 0075 { 0076 lastDescriptions.clear(); 0077 lastDescriptions << description; 0078 } 0079 0080 return description; 0081 } 0082 0083 // --------------------------------------------------------------------------- 0084 0085 ThumbnailLoadThread::ThumbnailLoadThread(QObject* const parent) 0086 : ManagedLoadSaveThread(parent), 0087 d (new Private) 0088 { 0089 static_d->firstThreadCreated = true; 0090 d->creator = new ThumbnailCreator(static_d->storageMethod); 0091 0092 if (static_d->provider) 0093 { 0094 d->creator->setThumbnailInfoProvider(static_d->provider); 0095 } 0096 0097 d->creator->setOnlyLargeThumbnails(true); 0098 d->creator->setRemoveAlphaChannel(true); 0099 0100 connect(this, SIGNAL(thumbnailsAvailable()), 0101 this, SLOT(slotThumbnailsAvailable())); 0102 } 0103 0104 ThumbnailLoadThread::~ThumbnailLoadThread() 0105 { 0106 shutDown(); 0107 0108 delete d->creator; 0109 delete d; 0110 } 0111 0112 ThumbnailLoadThread* ThumbnailLoadThread::defaultIconViewThread() 0113 { 0114 return defaultIconViewObject; 0115 } 0116 0117 ThumbnailLoadThread* ThumbnailLoadThread::defaultThread() 0118 { 0119 return defaultObject; 0120 } 0121 0122 void ThumbnailLoadThread::cleanUp() 0123 { 0124 // NOTE: Nothing to do with Qt5 and Q_GLOBAL_STATIC. Qt clean up all automatically at end of application instance. 0125 // But stopping all running tasks to prevent a crash at end. 0126 0127 defaultIconViewThread()->stopAllTasks(); 0128 defaultThread()->stopAllTasks(); 0129 0130 defaultIconViewThread()->wait(); 0131 defaultThread()->wait(); 0132 } 0133 0134 void ThumbnailLoadThread::initializeThumbnailDatabase(const DbEngineParameters& params, ThumbnailInfoProvider* const provider) 0135 { 0136 if (static_d->firstThreadCreated) 0137 { 0138 qCDebug(DIGIKAM_GENERAL_LOG) << "Call initializeThumbnailDatabase at application start. " 0139 "There are already thumbnail loading threads created, " 0140 "and these will not be switched to use the database. "; 0141 } 0142 0143 ThumbsDbAccess::setParameters(params); 0144 0145 if (ThumbsDbAccess::checkReadyForUse(nullptr)) 0146 { 0147 qCDebug(DIGIKAM_GENERAL_LOG) << "Thumbnails database ready for use"; 0148 static_d->storageMethod = ThumbnailCreator::ThumbnailDatabase; 0149 static_d->provider = provider; 0150 } 0151 else 0152 { 0153 QMessageBox::information(qApp->activeWindow(), i18nc("@title:window", "Failed to Initialize Thumbnails Database"), 0154 i18n("Error message: %1", ThumbsDbAccess().lastError())); 0155 } 0156 } 0157 0158 void ThumbnailLoadThread::setDisplayingWidget(QWidget* const widget) 0159 { 0160 static_d->profile = IccManager::displayProfile(widget); 0161 } 0162 0163 void ThumbnailLoadThread::setThumbnailSize(int size, bool forFace) 0164 { 0165 d->size = size; 0166 0167 if (forFace) 0168 { 0169 d->creator->setThumbnailSize(size); 0170 } 0171 } 0172 0173 int ThumbnailLoadThread::maximumThumbnailSize() 0174 { 0175 return ThumbnailSize::maxThumbsSize(); 0176 } 0177 0178 int ThumbnailLoadThread::maximumThumbnailPixmapSize(bool highlight) 0179 { 0180 if (highlight) 0181 { 0182 return ThumbnailSize::maxThumbsSize(); 0183 } 0184 else 0185 { 0186 return ThumbnailSize::maxThumbsSize() + 2; // see slotThumbnailLoaded 0187 } 0188 } 0189 0190 void ThumbnailLoadThread::setSendSurrogatePixmap(bool send) 0191 { 0192 d->sendSurrogate = send; 0193 } 0194 0195 void ThumbnailLoadThread::setPixmapRequested(bool wantPixmap) 0196 { 0197 d->wantPixmap = wantPixmap; 0198 } 0199 0200 void ThumbnailLoadThread::setHighlightPixmap(bool highlight) 0201 { 0202 d->highlight = highlight; 0203 } 0204 0205 ThumbnailCreator* ThumbnailLoadThread::thumbnailCreator() const 0206 { 0207 return d->creator; 0208 } 0209 0210 int ThumbnailLoadThread::thumbnailToPixmapSize(int size) const 0211 { 0212 return d->pixmapSizeForThumbnailSize(size); 0213 } 0214 0215 int ThumbnailLoadThread::thumbnailToPixmapSize(bool withHighlight, int size) 0216 { 0217 if (withHighlight && (size >= 10)) 0218 { 0219 return (size + 2); 0220 } 0221 0222 return size; 0223 } 0224 0225 int ThumbnailLoadThread::pixmapToThumbnailSize(int size) const 0226 { 0227 return d->thumbnailSizeForPixmapSize(size); 0228 } 0229 0230 bool ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, 0231 int size, 0232 QPixmap* retPixmap, 0233 bool emitSignal, 0234 const QRect& detailRect, 0235 bool onlyStorage) 0236 { 0237 const QPixmap* pix = nullptr; 0238 LoadingDescription description; 0239 0240 if (detailRect.isNull()) 0241 { 0242 description = d->createLoadingDescription(identifier, size); 0243 } 0244 else 0245 { 0246 description = d->createLoadingDescription(identifier, size, detailRect); 0247 } 0248 0249 if (onlyStorage) 0250 { 0251 description.previewParameters.flags |= LoadingDescription::PreviewParameters::OnlyFromStorage; 0252 } 0253 0254 QString cacheKey = description.cacheKey(); 0255 0256 { 0257 LoadingCache* const cache = LoadingCache::cache(); 0258 LoadingCache::CacheLock lock(cache); 0259 pix = cache->retrieveThumbnailPixmap(cacheKey); 0260 } 0261 0262 if (pix) 0263 { 0264 if (retPixmap) 0265 { 0266 *retPixmap = *pix; 0267 } 0268 0269 if (emitSignal) 0270 { 0271 load(description); 0272 Q_EMIT signalThumbnailLoaded(description, QPixmap(*pix)); 0273 } 0274 0275 return true; 0276 } 0277 0278 { 0279 // If there is a result waiting for conversion to pixmap, return false - pixmap will come shortly 0280 0281 QMutexLocker lock(&d->resultsMutex); 0282 0283 if (d->collectedResults.contains(cacheKey)) 0284 { 0285 return false; 0286 } 0287 } 0288 0289 load(description); 0290 0291 return false; 0292 } 0293 0294 // --- Normal thumbnails --- 0295 0296 bool ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, 0297 QPixmap& retPixmap, int size, bool onlyStorage) 0298 { 0299 return find(identifier, size, &retPixmap, false, QRect(), onlyStorage); 0300 } 0301 0302 bool ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, QPixmap& retPixmap) 0303 { 0304 return find(identifier, retPixmap, d->size); 0305 } 0306 0307 void ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier) 0308 { 0309 find(identifier, d->size); 0310 } 0311 0312 void ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, int size) 0313 { 0314 find(identifier, size, nullptr, true, QRect()); 0315 } 0316 0317 void ThumbnailLoadThread::findGroup(QList<ThumbnailIdentifier>& identifiers) 0318 { 0319 findGroup(identifiers, d->size); 0320 } 0321 0322 void ThumbnailLoadThread::findGroup(QList<ThumbnailIdentifier>& identifiers, int size) 0323 { 0324 if (!checkSize(size)) 0325 { 0326 return; 0327 } 0328 0329 QList<LoadingDescription> descriptions = d->makeDescriptions(identifiers, size); 0330 ManagedLoadSaveThread::prependThumbnailGroup(descriptions); 0331 } 0332 0333 // --- Detail thumbnails --- 0334 0335 bool ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, const QRect& rect, QPixmap& pixmap) 0336 { 0337 return find(identifier, rect, pixmap, d->size); 0338 } 0339 0340 bool ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, 0341 const QRect& rect, QPixmap& pixmap, int size, bool onlyStorage) 0342 { 0343 return find(identifier, size, &pixmap, false, rect, onlyStorage); 0344 } 0345 0346 void ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, const QRect& rect) 0347 { 0348 find(identifier, rect, d->size); 0349 } 0350 0351 void ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, const QRect& rect, int size) 0352 { 0353 find(identifier, size, nullptr, true, rect); 0354 } 0355 0356 void ThumbnailLoadThread::findGroup(const QList<QPair<ThumbnailIdentifier, QRect> >& idsAndRects) 0357 { 0358 findGroup(idsAndRects, d->size); 0359 } 0360 0361 void ThumbnailLoadThread::findGroup(const QList<QPair<ThumbnailIdentifier, QRect> >& idsAndRects, int size) 0362 { 0363 if (!checkSize(size)) 0364 { 0365 return; 0366 } 0367 0368 QList<LoadingDescription> descriptions = d->makeDescriptions(idsAndRects, size); 0369 ManagedLoadSaveThread::prependThumbnailGroup(descriptions); 0370 } 0371 0372 // --- Preloading --- 0373 0374 void ThumbnailLoadThread::preload(const ThumbnailIdentifier& identifier) 0375 { 0376 preload(identifier, d->size); 0377 } 0378 0379 void ThumbnailLoadThread::preload(const ThumbnailIdentifier& identifier, int size) 0380 { 0381 LoadingDescription description = d->createLoadingDescription(identifier, size); 0382 0383 if (d->checkDescription(description)) 0384 { 0385 load(description, true); 0386 } 0387 } 0388 0389 void ThumbnailLoadThread::preloadGroup(QList<ThumbnailIdentifier>& identifiers) 0390 { 0391 preloadGroup(identifiers, d->size); 0392 } 0393 0394 void ThumbnailLoadThread::preloadGroup(QList<ThumbnailIdentifier>& identifiers, int size) 0395 { 0396 if (!checkSize(size)) 0397 { 0398 return; 0399 } 0400 0401 QList<LoadingDescription> descriptions = d->makeDescriptions(identifiers, size); 0402 ManagedLoadSaveThread::preloadThumbnailGroup(descriptions); 0403 } 0404 0405 void ThumbnailLoadThread::pregenerateGroup(const QList<ThumbnailIdentifier>& identifiers) 0406 { 0407 pregenerateGroup(identifiers, d->size); 0408 } 0409 0410 void ThumbnailLoadThread::pregenerateGroup(const QList<ThumbnailIdentifier>& identifiers, int size) 0411 { 0412 if (!checkSize(size)) 0413 { 0414 return; 0415 } 0416 0417 QList<LoadingDescription> descriptions = d->makeDescriptions(identifiers, size); 0418 0419 for (int i = 0 ; i < descriptions.size() ; ++i) 0420 { 0421 descriptions[i].previewParameters.flags |= LoadingDescription::PreviewParameters::OnlyPregenerate; 0422 } 0423 0424 ManagedLoadSaveThread::preloadThumbnailGroup(descriptions); 0425 } 0426 0427 // --- Basic load() --- 0428 0429 void ThumbnailLoadThread::load(const LoadingDescription& desc) 0430 { 0431 load(desc, false); 0432 } 0433 0434 void ThumbnailLoadThread::load(const LoadingDescription& description, bool preload) 0435 { 0436 if (!checkSize(description.previewParameters.size)) 0437 { 0438 return; 0439 } 0440 0441 if (preload) 0442 { 0443 ManagedLoadSaveThread::preloadThumbnail(description); 0444 } 0445 else 0446 { 0447 ManagedLoadSaveThread::loadThumbnail(description); 0448 } 0449 } 0450 0451 QList<LoadingDescription> ThumbnailLoadThread::lastDescriptions() const 0452 { 0453 return d->lastDescriptions; 0454 } 0455 0456 bool ThumbnailLoadThread::checkSize(int size) 0457 { 0458 size = d->thumbnailSizeForPixmapSize(size); 0459 double dpr = qApp->devicePixelRatio(); 0460 int maxThumbSize = (dpr > 1.0) ? ThumbnailSize::MAX 0461 : ThumbnailSize::maxThumbsSize(); 0462 0463 if (size <= 0) 0464 { 0465 qCDebug(DIGIKAM_GENERAL_LOG) << "ThumbnailLoadThread::load: No thumbnail size specified. Refusing to load thumbnail."; 0466 return false; 0467 } 0468 else if (size > maxThumbSize) 0469 { 0470 qCDebug(DIGIKAM_GENERAL_LOG) << "ThumbnailLoadThread::load: Thumbnail size " << size 0471 << " is larger than " << maxThumbSize << ". Refusing to load."; 0472 return false; 0473 } 0474 0475 return true; 0476 } 0477 0478 // --- Receiving --- 0479 0480 /** 0481 * virtual method overridden from LoadSaveNotifier, implemented first by LoadSaveThread 0482 * called by ThumbnailTask from working thread 0483 */ 0484 void ThumbnailLoadThread::thumbnailLoaded(const LoadingDescription& loadingDescription, const QImage& img) 0485 { 0486 // call parent to send signalThumbnailLoaded(LoadingDescription, QImage) - signal is part of public API 0487 0488 ManagedLoadSaveThread::thumbnailLoaded(loadingDescription, img); 0489 0490 if (!d->wantPixmap) 0491 { 0492 return; 0493 } 0494 0495 // Store result in our list and fire one signal 0496 // This means there can be several results per pixmap, 0497 // to speed up cases where inter-thread communication is the limiting factor 0498 0499 QMutexLocker lock(&d->resultsMutex); 0500 d->collectedResults.insert(loadingDescription.cacheKey(), ThumbnailResult(loadingDescription, img)); 0501 0502 // only sent signal when flag indicates there is no signal on the way currently 0503 0504 if (!d->notifiedForResults) 0505 { 0506 d->notifiedForResults = true; 0507 Q_EMIT thumbnailsAvailable(); 0508 } 0509 } 0510 0511 void ThumbnailLoadThread::slotThumbnailsAvailable() 0512 { 0513 // harvest collected results safely into our thread 0514 0515 QList<ThumbnailResult> results; 0516 { 0517 QMutexLocker lock(&d->resultsMutex); 0518 results = d->collectedResults.values(); 0519 d->collectedResults.clear(); 0520 0521 // reset flag so that for next result, the signal is sent again 0522 0523 d->notifiedForResults = false; 0524 } 0525 0526 Q_FOREACH (const ThumbnailResult& result, results) 0527 { 0528 slotThumbnailLoaded(result.loadingDescription, result.image); 0529 } 0530 } 0531 0532 void ThumbnailLoadThread::slotThumbnailLoaded(const LoadingDescription& description, const QImage& thumb) 0533 { 0534 QPixmap pix; 0535 0536 if (thumb.isNull()) 0537 { 0538 pix = surrogatePixmap(description); 0539 } 0540 else 0541 { 0542 int w = thumb.width(); 0543 int h = thumb.height(); 0544 0545 // highlight only when requested and when thumbnail 0546 // width and height are greater than 10 0547 0548 if (d->highlight && ((w >= 10) && (h >= 10))) 0549 { 0550 pix = QPixmap(w + 2, h + 2); 0551 QPainter p(&pix); 0552 p.setPen(QPen(Qt::black, 1)); 0553 p.drawRect(0, 0, w + 1, h + 1); 0554 p.drawImage(1, 1, thumb); 0555 } 0556 else 0557 { 0558 pix = QPixmap::fromImage(thumb); 0559 } 0560 } 0561 0562 // put into cache 0563 0564 if (!pix.isNull()) 0565 { 0566 LoadingCache* const cache = LoadingCache::cache(); 0567 LoadingCache::CacheLock lock(cache); 0568 cache->putThumbnail(description.cacheKey(), pix, description.filePath); 0569 } 0570 0571 Q_EMIT signalThumbnailLoaded(description, pix); 0572 } 0573 0574 QPixmap ThumbnailLoadThread::surrogatePixmap(const LoadingDescription& description) 0575 { 0576 if (!d->sendSurrogate) 0577 { 0578 return QPixmap(); 0579 } 0580 0581 QPixmap pix; 0582 0583 QMimeType mimeType = QMimeDatabase().mimeTypeForFile(description.filePath); 0584 0585 if (mimeType.isValid()) 0586 { 0587 pix = QIcon::fromTheme(mimeType.genericIconName()).pixmap(128); 0588 } 0589 0590 if (pix.isNull()) 0591 { 0592 pix = QIcon::fromTheme(QLatin1String("application-x-zerosize")).pixmap(128); 0593 } 0594 0595 if (pix.isNull()) 0596 { 0597 // give up 0598 return QPixmap(); 0599 } 0600 0601 // Resize icon to the right size depending of current settings. 0602 0603 QSize size(pix.size()); 0604 size.scale(description.previewParameters.size, description.previewParameters.size, Qt::KeepAspectRatio); 0605 0606 if (!pix.isNull() && (size.width() < pix.width()) && (size.height() < pix.height())) 0607 { 0608 // only scale down 0609 // do not scale up, looks bad 0610 0611 pix = pix.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 0612 } 0613 0614 return pix; 0615 } 0616 0617 // --- Utilities --- 0618 0619 void ThumbnailLoadThread::storeDetailThumbnail(const QString& filePath, const QRect& detailRect, const QImage& image, bool isFace) 0620 { 0621 Q_UNUSED(isFace); 0622 d->creator->storeDetailThumbnail(filePath, detailRect, image); 0623 } 0624 0625 int ThumbnailLoadThread::storedSize() const 0626 { 0627 return d->creator->storedSize(); 0628 } 0629 0630 void ThumbnailLoadThread::deleteThumbnail(const QString& filePath) 0631 { 0632 { 0633 LoadingCache* const cache = LoadingCache::cache(); 0634 LoadingCache::CacheLock lock(cache); 0635 QStringList possibleKeys = LoadingDescription::possibleThumbnailCacheKeys(filePath); 0636 0637 Q_FOREACH (const QString& cacheKey, possibleKeys) 0638 { 0639 cache->removeThumbnail(cacheKey); 0640 } 0641 } 0642 0643 ThumbnailCreator creator(static_d->storageMethod); 0644 0645 if (static_d->provider) 0646 { 0647 creator.setThumbnailInfoProvider(static_d->provider); 0648 } 0649 0650 creator.deleteThumbnailsFromDisk(filePath); 0651 } 0652 0653 ThumbnailImageCatcher::ThumbnailImageCatcher(QObject* const parent) 0654 : QObject(parent), 0655 d (new Private) 0656 { 0657 } 0658 0659 ThumbnailImageCatcher::ThumbnailImageCatcher(ThumbnailLoadThread* const thread, QObject* const parent) 0660 : QObject(parent), 0661 d (new Private) 0662 { 0663 setThumbnailLoadThread(thread); 0664 } 0665 0666 ThumbnailImageCatcher::~ThumbnailImageCatcher() 0667 { 0668 delete d; 0669 } 0670 0671 ThumbnailLoadThread* ThumbnailImageCatcher::thread() const 0672 { 0673 return d->thread; 0674 } 0675 0676 void ThumbnailImageCatcher::setThumbnailLoadThread(ThumbnailLoadThread* const thread) 0677 { 0678 if (d->thread == thread) 0679 { 0680 return; 0681 } 0682 0683 d->state = Private::Inactive; 0684 0685 if (d->thread) 0686 { 0687 disconnect(d->thread, SIGNAL(signalThumbnailLoaded(LoadingDescription,QImage)), 0688 this, SLOT(slotThumbnailLoaded(LoadingDescription,QImage))); 0689 } 0690 0691 d->thread = thread; 0692 0693 { 0694 QMutexLocker lock(&d->mutex); 0695 d->reset(); 0696 } 0697 0698 if (d->thread) 0699 { 0700 connect(d->thread, SIGNAL(signalThumbnailLoaded(LoadingDescription,QImage)), 0701 this, SLOT(slotThumbnailLoaded(LoadingDescription,QImage)), 0702 Qt::DirectConnection); 0703 } 0704 } 0705 0706 void ThumbnailImageCatcher::setActive(bool active) 0707 { 0708 if (d->active == active) 0709 { 0710 return; 0711 } 0712 0713 if (!active) 0714 { 0715 cancel(); 0716 } 0717 0718 QMutexLocker lock(&d->mutex); 0719 d->active = active; 0720 d->reset(); 0721 } 0722 0723 void ThumbnailImageCatcher::cancel() 0724 { 0725 QMutexLocker lock(&d->mutex); 0726 0727 if (d->state == Private::Waiting) 0728 { 0729 d->state = Private::Quitting; 0730 d->condVar.wakeOne(); 0731 } 0732 } 0733 0734 void ThumbnailImageCatcher::slotThumbnailLoaded(const LoadingDescription& description, const QImage& image) 0735 { 0736 // We are in the thumbnail thread here, DirectConnection! 0737 0738 QMutexLocker lock(&d->mutex); 0739 0740 switch (d->state) 0741 { 0742 case Private::Inactive: 0743 { 0744 break; 0745 } 0746 0747 case Private::Accepting: 0748 { 0749 d->intermediate << Private::CatcherResult(description, image); 0750 break; 0751 } 0752 0753 case Private::Waiting: 0754 { 0755 d->harvest(description, image); 0756 break; 0757 } 0758 0759 case Private::Quitting: 0760 { 0761 break; 0762 } 0763 } 0764 } 0765 0766 int ThumbnailImageCatcher::enqueue() 0767 { 0768 QList<LoadingDescription> descriptions = d->thread->lastDescriptions(); 0769 0770 QMutexLocker lock(&d->mutex); 0771 0772 Q_FOREACH (const LoadingDescription& description, descriptions) 0773 { 0774 d->tasks << Private::CatcherResult(description); 0775 } 0776 0777 return descriptions.size(); 0778 } 0779 0780 QList<QImage> ThumbnailImageCatcher::waitForThumbnails() 0781 { 0782 if (!d->thread || d->tasks.isEmpty() || !d->active) 0783 { 0784 return QList<QImage>(); 0785 } 0786 0787 QMutexLocker lock(&d->mutex); 0788 d->state = Private::Waiting; 0789 0790 // first, handle results received between request and calling this method 0791 0792 Q_FOREACH (const Private::CatcherResult& result, d->intermediate) 0793 { 0794 d->harvest(result.description, result.image); 0795 } 0796 0797 d->intermediate.clear(); 0798 0799 // Now wait for the rest to arrive. If already finished, state will be Quitting 0800 0801 while (d->state == Private::Waiting) 0802 { 0803 d->condVar.wait(&d->mutex); 0804 } 0805 0806 QList<QImage> result; 0807 0808 Q_FOREACH (const Private::CatcherResult& task, d->tasks) 0809 { 0810 result << task.image; 0811 } 0812 0813 d->reset(); 0814 0815 return result; 0816 } 0817 0818 } // namespace Digikam 0819 0820 #include "moc_thumbnailloadthread.cpp"