1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2007-06-05
7 * Description : Thumbnail loading
8 *
9 * Copyright (C) 2006-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
10 * Copyright (C) 2005-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
11 * Copyright (C) 2015 by Mohamed_Anwer <m_dot_anwer at gmx dot com>
12 *
13 * This program is free software; you can redistribute it
14 * and/or modify it under the terms of the GNU General
15 * Public License as published by the Free Software Foundation;
16 * either version 2, or (at your option)
17 * any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * ============================================================ */
25
26 #include "thumbnailloadthread_p.h"
27
28 namespace Digikam
29 {
30
Q_GLOBAL_STATIC(ThumbnailLoadThreadStaticPriv,static_d)31 Q_GLOBAL_STATIC(ThumbnailLoadThreadStaticPriv, static_d)
32 Q_GLOBAL_STATIC(ThumbnailLoadThread, defaultObject)
33 Q_GLOBAL_STATIC(ThumbnailLoadThread, defaultIconViewObject)
34
35 // --- Creating loading descriptions ---
36
37 LoadingDescription ThumbnailLoadThread::Private::createLoadingDescription(const ThumbnailIdentifier& identifier,
38 int size,
39 bool setLastDescription)
40 {
41 size = thumbnailSizeForPixmapSize(size);
42
43 LoadingDescription description(identifier.filePath, PreviewSettings(), size,
44 LoadingDescription::NoColorConversion,
45 LoadingDescription::PreviewParameters::Thumbnail);
46 description.previewParameters.storageReference = identifier.id;
47
48 if (IccSettings::instance()->useManagedPreviews())
49 {
50 description.postProcessingParameters.colorManagement = LoadingDescription::ConvertForDisplay;
51 description.postProcessingParameters.setProfile(static_d->profile);
52 }
53
54 if (setLastDescription)
55 {
56 lastDescriptions.clear();
57 lastDescriptions << description;
58 }
59
60 return description;
61 }
62
createLoadingDescription(const ThumbnailIdentifier & identifier,int size,const QRect & detailRect,bool setLastDescription)63 LoadingDescription ThumbnailLoadThread::Private::createLoadingDescription(const ThumbnailIdentifier& identifier,
64 int size,
65 const QRect& detailRect,
66 bool setLastDescription)
67 {
68 size = thumbnailSizeForPixmapSize(size);
69
70 LoadingDescription description(identifier.filePath, PreviewSettings(), size,
71 LoadingDescription::NoColorConversion,
72 LoadingDescription::PreviewParameters::DetailThumbnail);
73
74 description.previewParameters.storageReference = identifier.id;
75 description.previewParameters.extraParameter = detailRect;
76
77 if (IccSettings::instance()->useManagedPreviews())
78 {
79 description.postProcessingParameters.colorManagement = LoadingDescription::ConvertForDisplay;
80 description.postProcessingParameters.setProfile(static_d->profile);
81 }
82
83 if (setLastDescription)
84 {
85 lastDescriptions.clear();
86 lastDescriptions << description;
87 }
88
89 return description;
90 }
91
92 // ---------------------------------------------------------------------------
93
ThumbnailLoadThread(QObject * const parent)94 ThumbnailLoadThread::ThumbnailLoadThread(QObject* const parent)
95 : ManagedLoadSaveThread(parent),
96 d (new Private)
97 {
98 static_d->firstThreadCreated = true;
99 d->creator = new ThumbnailCreator(static_d->storageMethod);
100
101 if (static_d->provider)
102 {
103 d->creator->setThumbnailInfoProvider(static_d->provider);
104 }
105
106 d->creator->setOnlyLargeThumbnails(true);
107 d->creator->setRemoveAlphaChannel(true);
108
109 connect(this, SIGNAL(thumbnailsAvailable()),
110 this, SLOT(slotThumbnailsAvailable()));
111 }
112
~ThumbnailLoadThread()113 ThumbnailLoadThread::~ThumbnailLoadThread()
114 {
115 shutDown();
116
117 delete d->creator;
118 delete d;
119 }
120
defaultIconViewThread()121 ThumbnailLoadThread* ThumbnailLoadThread::defaultIconViewThread()
122 {
123 return defaultIconViewObject;
124 }
125
defaultThread()126 ThumbnailLoadThread* ThumbnailLoadThread::defaultThread()
127 {
128 return defaultObject;
129 }
130
cleanUp()131 void ThumbnailLoadThread::cleanUp()
132 {
133 // NOTE: Nothing to do with Qt5 and Q_GLOBAL_STATIC. Qt clean up all automatically at end of application instance.
134 // But stopping all running tasks to prevent a crash at end.
135
136 defaultIconViewThread()->stopAllTasks();
137 defaultThread()->stopAllTasks();
138
139 defaultIconViewThread()->wait();
140 defaultThread()->wait();
141 }
142
initializeThumbnailDatabase(const DbEngineParameters & params,ThumbnailInfoProvider * const provider)143 void ThumbnailLoadThread::initializeThumbnailDatabase(const DbEngineParameters& params, ThumbnailInfoProvider* const provider)
144 {
145 if (static_d->firstThreadCreated)
146 {
147 qCDebug(DIGIKAM_GENERAL_LOG) << "Call initializeThumbnailDatabase at application start. "
148 "There are already thumbnail loading threads created, "
149 "and these will not be switched to use the database. ";
150 }
151
152 ThumbsDbAccess::setParameters(params);
153
154 if (ThumbsDbAccess::checkReadyForUse(nullptr))
155 {
156 qCDebug(DIGIKAM_GENERAL_LOG) << "Thumbnails database ready for use";
157 static_d->storageMethod = ThumbnailCreator::ThumbnailDatabase;
158 static_d->provider = provider;
159 }
160 else
161 {
162 QMessageBox::information(qApp->activeWindow(), i18n("Failed to initialize thumbnails database"),
163 i18n("Error message: %1", ThumbsDbAccess().lastError()));
164 }
165 }
166
setDisplayingWidget(QWidget * const widget)167 void ThumbnailLoadThread::setDisplayingWidget(QWidget* const widget)
168 {
169 static_d->profile = IccManager::displayProfile(widget);
170 }
171
setThumbnailSize(int size,bool forFace)172 void ThumbnailLoadThread::setThumbnailSize(int size, bool forFace)
173 {
174 d->size = size;
175
176 if (forFace)
177 {
178 d->creator->setThumbnailSize(size);
179 }
180 }
181
maximumThumbnailSize()182 int ThumbnailLoadThread::maximumThumbnailSize()
183 {
184 return ThumbnailSize::maxThumbsSize();
185 }
186
maximumThumbnailPixmapSize(bool highlight)187 int ThumbnailLoadThread::maximumThumbnailPixmapSize(bool highlight)
188 {
189 if (highlight)
190 {
191 return ThumbnailSize::maxThumbsSize();
192 }
193 else
194 {
195 return ThumbnailSize::maxThumbsSize() + 2; // see slotThumbnailLoaded
196 }
197 }
198
setSendSurrogatePixmap(bool send)199 void ThumbnailLoadThread::setSendSurrogatePixmap(bool send)
200 {
201 d->sendSurrogate = send;
202 }
203
setPixmapRequested(bool wantPixmap)204 void ThumbnailLoadThread::setPixmapRequested(bool wantPixmap)
205 {
206 d->wantPixmap = wantPixmap;
207 }
208
setHighlightPixmap(bool highlight)209 void ThumbnailLoadThread::setHighlightPixmap(bool highlight)
210 {
211 d->highlight = highlight;
212 }
213
thumbnailCreator() const214 ThumbnailCreator* ThumbnailLoadThread::thumbnailCreator() const
215 {
216 return d->creator;
217 }
218
thumbnailToPixmapSize(int size) const219 int ThumbnailLoadThread::thumbnailToPixmapSize(int size) const
220 {
221 return d->pixmapSizeForThumbnailSize(size);
222 }
223
thumbnailToPixmapSize(bool withHighlight,int size)224 int ThumbnailLoadThread::thumbnailToPixmapSize(bool withHighlight, int size)
225 {
226 if (withHighlight && (size >= 10))
227 {
228 return (size + 2);
229 }
230
231 return size;
232 }
233
pixmapToThumbnailSize(int size) const234 int ThumbnailLoadThread::pixmapToThumbnailSize(int size) const
235 {
236 return d->thumbnailSizeForPixmapSize(size);
237 }
238
find(const ThumbnailIdentifier & identifier,int size,QPixmap * retPixmap,bool emitSignal,const QRect & detailRect)239 bool ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier,
240 int size,
241 QPixmap* retPixmap,
242 bool emitSignal,
243 const QRect& detailRect)
244 {
245 const QPixmap* pix = nullptr;
246 LoadingDescription description;
247
248 if (detailRect.isNull())
249 {
250 description = d->createLoadingDescription(identifier, size);
251 }
252 else
253 {
254 description = d->createLoadingDescription(identifier, size, detailRect);
255 }
256
257 QString cacheKey = description.cacheKey();
258
259 {
260 LoadingCache* const cache = LoadingCache::cache();
261 LoadingCache::CacheLock lock(cache);
262 pix = cache->retrieveThumbnailPixmap(cacheKey);
263 }
264
265 if (pix)
266 {
267 if (retPixmap)
268 {
269 *retPixmap = *pix;
270 }
271
272 if (emitSignal)
273 {
274 load(description);
275 emit signalThumbnailLoaded(description, QPixmap(*pix));
276 }
277
278 return true;
279 }
280
281 {
282 // If there is a result waiting for conversion to pixmap, return false - pixmap will come shortly
283
284 QMutexLocker lock(&d->resultsMutex);
285
286 if (d->collectedResults.contains(cacheKey))
287 {
288 return false;
289 }
290 }
291
292 load(description);
293
294 return false;
295 }
296
297 // --- Normal thumbnails ---
298
find(const ThumbnailIdentifier & identifier,QPixmap & retPixmap,int size)299 bool ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, QPixmap& retPixmap, int size)
300 {
301 return find(identifier, size, &retPixmap, false, QRect());
302 }
303
find(const ThumbnailIdentifier & identifier,QPixmap & retPixmap)304 bool ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, QPixmap& retPixmap)
305 {
306 return find(identifier, retPixmap, d->size);
307 }
308
find(const ThumbnailIdentifier & identifier)309 void ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier)
310 {
311 find(identifier, d->size);
312 }
313
find(const ThumbnailIdentifier & identifier,int size)314 void ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, int size)
315 {
316 find(identifier, size, nullptr, true, QRect());
317 }
318
findGroup(QList<ThumbnailIdentifier> & identifiers)319 void ThumbnailLoadThread::findGroup(QList<ThumbnailIdentifier>& identifiers)
320 {
321 findGroup(identifiers, d->size);
322 }
323
findGroup(QList<ThumbnailIdentifier> & identifiers,int size)324 void ThumbnailLoadThread::findGroup(QList<ThumbnailIdentifier>& identifiers, int size)
325 {
326 if (!checkSize(size))
327 {
328 return;
329 }
330
331 QList<LoadingDescription> descriptions = d->makeDescriptions(identifiers, size);
332 ManagedLoadSaveThread::prependThumbnailGroup(descriptions);
333 }
334
335 // --- Detail thumbnails ---
336
find(const ThumbnailIdentifier & identifier,const QRect & rect,QPixmap & pixmap)337 bool ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, const QRect& rect, QPixmap& pixmap)
338 {
339 return find(identifier, rect, pixmap, d->size);
340 }
341
find(const ThumbnailIdentifier & identifier,const QRect & rect,QPixmap & pixmap,int size)342 bool ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, const QRect& rect, QPixmap& pixmap, int size)
343 {
344 return find(identifier, size, &pixmap, false, rect);
345 }
346
find(const ThumbnailIdentifier & identifier,const QRect & rect)347 void ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, const QRect& rect)
348 {
349 find(identifier, rect, d->size);
350 }
351
find(const ThumbnailIdentifier & identifier,const QRect & rect,int size)352 void ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, const QRect& rect, int size)
353 {
354 find(identifier, size, nullptr, true, rect);
355 }
356
findGroup(const QList<QPair<ThumbnailIdentifier,QRect>> & idsAndRects)357 void ThumbnailLoadThread::findGroup(const QList<QPair<ThumbnailIdentifier, QRect> >& idsAndRects)
358 {
359 findGroup(idsAndRects, d->size);
360 }
361
findGroup(const QList<QPair<ThumbnailIdentifier,QRect>> & idsAndRects,int size)362 void ThumbnailLoadThread::findGroup(const QList<QPair<ThumbnailIdentifier, QRect> >& idsAndRects, int size)
363 {
364 if (!checkSize(size))
365 {
366 return;
367 }
368
369 QList<LoadingDescription> descriptions = d->makeDescriptions(idsAndRects, size);
370 ManagedLoadSaveThread::prependThumbnailGroup(descriptions);
371 }
372
373 // --- Preloading ---
374
preload(const ThumbnailIdentifier & identifier)375 void ThumbnailLoadThread::preload(const ThumbnailIdentifier& identifier)
376 {
377 preload(identifier, d->size);
378 }
379
preload(const ThumbnailIdentifier & identifier,int size)380 void ThumbnailLoadThread::preload(const ThumbnailIdentifier& identifier, int size)
381 {
382 LoadingDescription description = d->createLoadingDescription(identifier, size);
383
384 if (d->checkDescription(description))
385 {
386 load(description, true);
387 }
388 }
389
preloadGroup(QList<ThumbnailIdentifier> & identifiers)390 void ThumbnailLoadThread::preloadGroup(QList<ThumbnailIdentifier>& identifiers)
391 {
392 preloadGroup(identifiers, d->size);
393 }
394
preloadGroup(QList<ThumbnailIdentifier> & identifiers,int size)395 void ThumbnailLoadThread::preloadGroup(QList<ThumbnailIdentifier>& identifiers, int size)
396 {
397 if (!checkSize(size))
398 {
399 return;
400 }
401
402 QList<LoadingDescription> descriptions = d->makeDescriptions(identifiers, size);
403 ManagedLoadSaveThread::preloadThumbnailGroup(descriptions);
404 }
405
pregenerateGroup(const QList<ThumbnailIdentifier> & identifiers)406 void ThumbnailLoadThread::pregenerateGroup(const QList<ThumbnailIdentifier>& identifiers)
407 {
408 pregenerateGroup(identifiers, d->size);
409 }
410
pregenerateGroup(const QList<ThumbnailIdentifier> & identifiers,int size)411 void ThumbnailLoadThread::pregenerateGroup(const QList<ThumbnailIdentifier>& identifiers, int size)
412 {
413 if (!checkSize(size))
414 {
415 return;
416 }
417
418 QList<LoadingDescription> descriptions = d->makeDescriptions(identifiers, size);
419
420 for (int i = 0 ; i < descriptions.size() ; ++i)
421 {
422 descriptions[i].previewParameters.flags |= LoadingDescription::PreviewParameters::OnlyPregenerate;
423 }
424
425 ManagedLoadSaveThread::preloadThumbnailGroup(descriptions);
426 }
427
428 // --- Basic load() ---
429
load(const LoadingDescription & desc)430 void ThumbnailLoadThread::load(const LoadingDescription& desc)
431 {
432 load(desc, false);
433 }
434
load(const LoadingDescription & description,bool preload)435 void ThumbnailLoadThread::load(const LoadingDescription& description, bool preload)
436 {
437 if (!checkSize(description.previewParameters.size))
438 {
439 return;
440 }
441
442 if (preload)
443 {
444 ManagedLoadSaveThread::preloadThumbnail(description);
445 }
446 else
447 {
448 ManagedLoadSaveThread::loadThumbnail(description);
449 }
450 }
451
lastDescriptions() const452 QList<LoadingDescription> ThumbnailLoadThread::lastDescriptions() const
453 {
454 return d->lastDescriptions;
455 }
456
checkSize(int size)457 bool ThumbnailLoadThread::checkSize(int size)
458 {
459 size = d->thumbnailSizeForPixmapSize(size);
460 double ratio = qApp->devicePixelRatio();
461 int maxThumbSize = (ratio > 1.0) ? ThumbnailSize::MAX
462 : ThumbnailSize::maxThumbsSize();
463
464 if (size <= 0)
465 {
466 qCDebug(DIGIKAM_GENERAL_LOG) << "ThumbnailLoadThread::load: No thumbnail size specified. Refusing to load thumbnail.";
467 return false;
468 }
469 else if (size > maxThumbSize)
470 {
471 qCDebug(DIGIKAM_GENERAL_LOG) << "ThumbnailLoadThread::load: Thumbnail size " << size
472 << " is larger than " << maxThumbSize << ". Refusing to load.";
473 return false;
474 }
475
476 return true;
477 }
478
479 // --- Receiving ---
480
481 /**
482 * virtual method overridden from LoadSaveNotifier, implemented first by LoadSaveThread
483 * called by ThumbnailTask from working thread
484 */
thumbnailLoaded(const LoadingDescription & loadingDescription,const QImage & img)485 void ThumbnailLoadThread::thumbnailLoaded(const LoadingDescription& loadingDescription, const QImage& img)
486 {
487 // call parent to send signalThumbnailLoaded(LoadingDescription, QImage) - signal is part of public API
488
489 ManagedLoadSaveThread::thumbnailLoaded(loadingDescription, img);
490
491 if (!d->wantPixmap)
492 {
493 return;
494 }
495
496 // Store result in our list and fire one signal
497 // This means there can be several results per pixmap,
498 // to speed up cases where inter-thread communication is the limiting factor
499
500 QMutexLocker lock(&d->resultsMutex);
501 d->collectedResults.insert(loadingDescription.cacheKey(), ThumbnailResult(loadingDescription, img));
502
503 // only sent signal when flag indicates there is no signal on the way currently
504
505 if (!d->notifiedForResults)
506 {
507 d->notifiedForResults = true;
508 emit thumbnailsAvailable();
509 }
510 }
511
slotThumbnailsAvailable()512 void ThumbnailLoadThread::slotThumbnailsAvailable()
513 {
514 // harvest collected results safely into our thread
515
516 QList<ThumbnailResult> results;
517 {
518 QMutexLocker lock(&d->resultsMutex);
519 results = d->collectedResults.values();
520 d->collectedResults.clear();
521
522 // reset flag so that for next result, the signal is sent again
523
524 d->notifiedForResults = false;
525 }
526
527 foreach (const ThumbnailResult& result, results)
528 {
529 slotThumbnailLoaded(result.loadingDescription, result.image);
530 }
531 }
532
slotThumbnailLoaded(const LoadingDescription & description,const QImage & thumb)533 void ThumbnailLoadThread::slotThumbnailLoaded(const LoadingDescription& description, const QImage& thumb)
534 {
535 QPixmap pix;
536
537 if (thumb.isNull())
538 {
539 pix = surrogatePixmap(description);
540 }
541 else
542 {
543 int w = thumb.width();
544 int h = thumb.height();
545
546 // highlight only when requested and when thumbnail
547 // width and height are greater than 10
548
549 if (d->highlight && ((w >= 10) && (h >= 10)))
550 {
551 pix = QPixmap(w + 2, h + 2);
552 QPainter p(&pix);
553 p.setPen(QPen(Qt::black, 1));
554 p.drawRect(0, 0, w + 1, h + 1);
555 p.drawImage(1, 1, thumb);
556 }
557 else
558 {
559 pix = QPixmap::fromImage(thumb);
560 }
561 }
562
563 // put into cache
564
565 if (!pix.isNull())
566 {
567 LoadingCache* const cache = LoadingCache::cache();
568 LoadingCache::CacheLock lock(cache);
569 cache->putThumbnail(description.cacheKey(), pix, description.filePath);
570 }
571
572 emit signalThumbnailLoaded(description, pix);
573 }
574
surrogatePixmap(const LoadingDescription & description)575 QPixmap ThumbnailLoadThread::surrogatePixmap(const LoadingDescription& description)
576 {
577 if (!d->sendSurrogate)
578 {
579 return QPixmap();
580 }
581
582 QPixmap pix;
583
584 QMimeType mimeType = QMimeDatabase().mimeTypeForFile(description.filePath);
585
586 if (mimeType.isValid())
587 {
588 pix = QIcon::fromTheme(mimeType.genericIconName()).pixmap(128);
589 }
590
591 if (pix.isNull())
592 {
593 pix = QIcon::fromTheme(QLatin1String("application-x-zerosize")).pixmap(128);
594 }
595
596 if (pix.isNull())
597 {
598 // give up
599 return QPixmap();
600 }
601
602 // Resize icon to the right size depending of current settings.
603
604 QSize size(pix.size());
605 size.scale(description.previewParameters.size, description.previewParameters.size, Qt::KeepAspectRatio);
606
607 if (!pix.isNull() && (size.width() < pix.width()) && (size.height() < pix.height()))
608 {
609 // only scale down
610 // do not scale up, looks bad
611
612 pix = pix.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
613 }
614
615 return pix;
616 }
617
618 // --- Utilities ---
619
storeDetailThumbnail(const QString & filePath,const QRect & detailRect,const QImage & image,bool isFace)620 void ThumbnailLoadThread::storeDetailThumbnail(const QString& filePath, const QRect& detailRect, const QImage& image, bool isFace)
621 {
622 Q_UNUSED(isFace);
623 d->creator->storeDetailThumbnail(filePath, detailRect, image);
624 }
625
storedSize() const626 int ThumbnailLoadThread::storedSize() const
627 {
628 return d->creator->storedSize();
629 }
630
deleteThumbnail(const QString & filePath)631 void ThumbnailLoadThread::deleteThumbnail(const QString& filePath)
632 {
633 {
634 LoadingCache* const cache = LoadingCache::cache();
635 LoadingCache::CacheLock lock(cache);
636 QStringList possibleKeys = LoadingDescription::possibleThumbnailCacheKeys(filePath);
637
638 foreach (const QString& cacheKey, possibleKeys)
639 {
640 cache->removeThumbnail(cacheKey);
641 }
642 }
643
644 ThumbnailCreator creator(static_d->storageMethod);
645
646 if (static_d->provider)
647 {
648 creator.setThumbnailInfoProvider(static_d->provider);
649 }
650
651 creator.deleteThumbnailsFromDisk(filePath);
652 }
653
ThumbnailImageCatcher(QObject * const parent)654 ThumbnailImageCatcher::ThumbnailImageCatcher(QObject* const parent)
655 : QObject(parent),
656 d (new Private)
657 {
658 }
659
ThumbnailImageCatcher(ThumbnailLoadThread * const thread,QObject * const parent)660 ThumbnailImageCatcher::ThumbnailImageCatcher(ThumbnailLoadThread* const thread, QObject* const parent)
661 : QObject(parent),
662 d (new Private)
663 {
664 setThumbnailLoadThread(thread);
665 }
666
~ThumbnailImageCatcher()667 ThumbnailImageCatcher::~ThumbnailImageCatcher()
668 {
669 delete d;
670 }
671
thread() const672 ThumbnailLoadThread* ThumbnailImageCatcher::thread() const
673 {
674 return d->thread;
675 }
676
setThumbnailLoadThread(ThumbnailLoadThread * const thread)677 void ThumbnailImageCatcher::setThumbnailLoadThread(ThumbnailLoadThread* const thread)
678 {
679 if (d->thread == thread)
680 {
681 return;
682 }
683
684 d->state = Private::Inactive;
685
686 if (d->thread)
687 {
688 disconnect(d->thread, SIGNAL(signalThumbnailLoaded(LoadingDescription,QImage)),
689 this, SLOT(slotThumbnailLoaded(LoadingDescription,QImage)));
690 }
691
692 d->thread = thread;
693
694 {
695 QMutexLocker lock(&d->mutex);
696 d->reset();
697 }
698
699 if (d->thread)
700 {
701 connect(d->thread, SIGNAL(signalThumbnailLoaded(LoadingDescription,QImage)),
702 this, SLOT(slotThumbnailLoaded(LoadingDescription,QImage)),
703 Qt::DirectConnection);
704 }
705 }
706
setActive(bool active)707 void ThumbnailImageCatcher::setActive(bool active)
708 {
709 if (d->active == active)
710 {
711 return;
712 }
713
714 if (!active)
715 {
716 cancel();
717 }
718
719 QMutexLocker lock(&d->mutex);
720 d->active = active;
721 d->reset();
722 }
723
cancel()724 void ThumbnailImageCatcher::cancel()
725 {
726 QMutexLocker lock(&d->mutex);
727
728 if (d->state == Private::Waiting)
729 {
730 d->state = Private::Quitting;
731 d->condVar.wakeOne();
732 }
733 }
734
slotThumbnailLoaded(const LoadingDescription & description,const QImage & image)735 void ThumbnailImageCatcher::slotThumbnailLoaded(const LoadingDescription& description, const QImage& image)
736 {
737 // We are in the thumbnail thread here, DirectConnection!
738
739 QMutexLocker lock(&d->mutex);
740
741 switch (d->state)
742 {
743 case Private::Inactive:
744 {
745 break;
746 }
747
748 case Private::Accepting:
749 {
750 d->intermediate << Private::CatcherResult(description, image);
751 break;
752 }
753
754 case Private::Waiting:
755 {
756 d->harvest(description, image);
757 break;
758 }
759
760 case Private::Quitting:
761 {
762 break;
763 }
764 }
765 }
766
enqueue()767 int ThumbnailImageCatcher::enqueue()
768 {
769 QList<LoadingDescription> descriptions = d->thread->lastDescriptions();
770
771 QMutexLocker lock(&d->mutex);
772
773 foreach (const LoadingDescription& description, descriptions)
774 {
775 d->tasks << Private::CatcherResult(description);
776 }
777
778 return descriptions.size();
779 }
780
waitForThumbnails()781 QList<QImage> ThumbnailImageCatcher::waitForThumbnails()
782 {
783 if (!d->thread || d->tasks.isEmpty() || !d->active)
784 {
785 return QList<QImage>();
786 }
787
788 QMutexLocker lock(&d->mutex);
789 d->state = Private::Waiting;
790
791 // first, handle results received between request and calling this method
792
793 foreach (const Private::CatcherResult& result, d->intermediate)
794 {
795 d->harvest(result.description, result.image);
796 }
797
798 d->intermediate.clear();
799
800 // Now wait for the rest to arrive. If already finished, state will be Quitting
801
802 while (d->state == Private::Waiting)
803 {
804 d->condVar.wait(&d->mutex);
805 }
806
807 QList<QImage> result;
808
809 foreach (const Private::CatcherResult& task, d->tasks)
810 {
811 result << task.image;
812 }
813
814 d->reset();
815
816 return result;
817 }
818
819 } // namespace Digikam
820