diff --git a/core/libs/threadimageio/CMakeLists.txt b/core/libs/threadimageio/CMakeLists.txt index 9b6980f912..26b08b9835 100644 --- a/core/libs/threadimageio/CMakeLists.txt +++ b/core/libs/threadimageio/CMakeLists.txt @@ -1,58 +1,59 @@ # # Copyright (c) 2010-2019 by Gilles Caulier, # Copyright (c) 2015 by Veaceslav Munteanu, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. set(libthreadimageio_SRCS engine/dfileoperations.cpp engine/filereadwritelock.cpp engine/managedloadsavethread.cpp engine/sharedloadsavethread.cpp preview/previewloadthread.cpp preview/previewtask.cpp preview/previewsettings.cpp thumb/thumbnailbasic.cpp thumb/thumbnailcreator.cpp thumb/thumbnailcreator_freedesktop.cpp thumb/thumbnailcreator_database.cpp thumb/thumbnailcreator_engine.cpp thumb/thumbnailloadthread.cpp + thumb/thumbnailloadthread_p.cpp thumb/thumbnailtask.cpp thumb/thumbnailsize.cpp fileio/loadsavethread.cpp fileio/loadingdescription.cpp fileio/loadingcache.cpp fileio/loadingcacheinterface.cpp fileio/loadsavetask.cpp ) if(ENABLE_MEDIAPLAYER) set(libthreadimageio_SRCS ${libthreadimageio_SRCS} video/videostripfilter.cpp video/videodecoder.cpp video/videodecoder_p.cpp video/videothumbwriter.cpp video/videothumbnailer.cpp ) endif() include_directories( $ $ $ $ $ $ $ ) if(ENABLE_DBUS) include_directories($) endif() add_library(threadimageio_src OBJECT ${libthreadimageio_SRCS}) diff --git a/core/libs/threadimageio/thumb/thumbnailloadthread.cpp b/core/libs/threadimageio/thumb/thumbnailloadthread.cpp index 40b2bc5356..544448a311 100644 --- a/core/libs/threadimageio/thumb/thumbnailloadthread.cpp +++ b/core/libs/threadimageio/thumb/thumbnailloadthread.cpp @@ -1,1114 +1,794 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2007-06-05 * Description : Thumbnail loading * * Copyright (C) 2006-2011 by Marcel Wiesweg * Copyright (C) 2005-2019 by Gilles Caulier * Copyright (C) 2015 by Mohamed_Anwer * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ -#include "thumbnailloadthread.h" - -// Qt includes - -#include -#include -#include -#include -#include -#include -#include -#include - -// KDE includes - -#include - -// Local includes - -#include "digikam_debug.h" -#include "dbengineparameters.h" -#include "iccmanager.h" -#include "iccprofile.h" -#include "iccsettings.h" -#include "metaenginesettings.h" -#include "thumbsdbaccess.h" -#include "thumbnailsize.h" -#include "thumbnailtask.h" -#include "thumbnailcreator.h" +#include "thumbnailloadthread_p.h" namespace Digikam { -class Q_DECL_HIDDEN ThumbnailResult -{ - -public: - - explicit ThumbnailResult(const LoadingDescription& description, const QImage& image) - : loadingDescription(description), - image(image) - { - } - - LoadingDescription loadingDescription; - QImage image; -}; +Q_GLOBAL_STATIC(ThumbnailLoadThreadStaticPriv, static_d) +Q_GLOBAL_STATIC(ThumbnailLoadThread, defaultIconViewObject) +Q_GLOBAL_STATIC(ThumbnailLoadThread, defaultObject) +Q_GLOBAL_STATIC(ThumbnailLoadThread, defaultThumbBarObject) -// ------------------------------------------------------------------- +// --- Creating loading descriptions --- -class Q_DECL_HIDDEN ThumbnailLoadThreadStaticPriv +LoadingDescription ThumbnailLoadThread::Private::createLoadingDescription(const ThumbnailIdentifier& identifier, + int size, + bool setLastDescription) { -public: + size = thumbnailSizeForPixmapSize(size); + + LoadingDescription description(identifier.filePath, PreviewSettings(), size, + LoadingDescription::NoColorConversion, + LoadingDescription::PreviewParameters::Thumbnail); + description.previewParameters.storageReference = identifier.id; - explicit ThumbnailLoadThreadStaticPriv() - : firstThreadCreated(false), - storageMethod(ThumbnailCreator::FreeDesktopStandard), - provider(nullptr), - profile(IccProfile::sRGB()) + if (IccSettings::instance()->useManagedPreviews()) { + description.postProcessingParameters.colorManagement = LoadingDescription::ConvertForDisplay; + description.postProcessingParameters.setProfile(static_d->profile); } - ~ThumbnailLoadThreadStaticPriv() + if (setLastDescription) { - delete provider; + lastDescriptions.clear(); + lastDescriptions << description; } -public: - - bool firstThreadCreated; - - ThumbnailCreator::StorageMethod storageMethod; - ThumbnailInfoProvider* provider; - IccProfile profile; -}; - -Q_GLOBAL_STATIC(ThumbnailLoadThreadStaticPriv, static_d) - -// ------------------------------------------------------------------- + return description; +} -class Q_DECL_HIDDEN ThumbnailLoadThread::Private +LoadingDescription ThumbnailLoadThread::Private::createLoadingDescription(const ThumbnailIdentifier& identifier, + int size, + const QRect& detailRect, + bool setLastDescription) { + size = thumbnailSizeForPixmapSize(size); -public: + LoadingDescription description(identifier.filePath, PreviewSettings(), size, + LoadingDescription::NoColorConversion, + LoadingDescription::PreviewParameters::DetailThumbnail); + description.previewParameters.storageReference = identifier.id; - explicit Private() + description.previewParameters.extraParameter = detailRect; + + if (IccSettings::instance()->useManagedPreviews()) { - size = ThumbnailSize::maxThumbsSize(); - wantPixmap = true; - highlight = true; - sendSurrogate = true; - notifiedForResults = false; - creator = nullptr; + description.postProcessingParameters.colorManagement = LoadingDescription::ConvertForDisplay; + description.postProcessingParameters.setProfile(static_d->profile); } - bool wantPixmap; - bool highlight; - bool sendSurrogate; - bool notifiedForResults; - - int size; - - ThumbnailCreator* creator; - - QHash collectedResults; - QMutex resultsMutex; - - QList lastDescriptions; - -public: + if (setLastDescription) + { + lastDescriptions.clear(); + lastDescriptions << description; + } - LoadingDescription createLoadingDescription(const ThumbnailIdentifier& identifier, int size, bool setLastDescription = true); - LoadingDescription createLoadingDescription(const ThumbnailIdentifier& identifier, int size, - const QRect& detailRect, bool setLastDescription = true); - bool checkDescription(const LoadingDescription& description); - QList makeDescriptions(const QList& identifiers, int size); - QList makeDescriptions(const QList >& idsAndRects, int size); - bool hasHighlightingBorder() const; - int pixmapSizeForThumbnailSize(int thumbnailSize) const; - int thumbnailSizeForPixmapSize(int pixmapSize) const; -}; + return description; +} -Q_GLOBAL_STATIC(ThumbnailLoadThread, defaultIconViewObject) -Q_GLOBAL_STATIC(ThumbnailLoadThread, defaultObject) -Q_GLOBAL_STATIC(ThumbnailLoadThread, defaultThumbBarObject) +// --------------------------------------------------------------------------- ThumbnailLoadThread::ThumbnailLoadThread(QObject* const parent) : ManagedLoadSaveThread(parent), d(new Private) { static_d->firstThreadCreated = true; d->creator = new ThumbnailCreator(static_d->storageMethod); if (static_d->provider) { d->creator->setThumbnailInfoProvider(static_d->provider); } d->creator->setOnlyLargeThumbnails(true); d->creator->setRemoveAlphaChannel(true); connect(this, SIGNAL(thumbnailsAvailable()), this, SLOT(slotThumbnailsAvailable())); } ThumbnailLoadThread::~ThumbnailLoadThread() { shutDown(); delete d->creator; delete d; } ThumbnailLoadThread* ThumbnailLoadThread::defaultIconViewThread() { return defaultIconViewObject; } ThumbnailLoadThread* ThumbnailLoadThread::defaultThread() { return defaultObject; } ThumbnailLoadThread* ThumbnailLoadThread::defaultThumbBarThread() { return defaultThumbBarObject; } void ThumbnailLoadThread::cleanUp() { // NOTE : Nothing to do with Qt5 and Q_GLOBAL_STATIC. Qt clean up all automatically at end of application instance. // But stopping all running tasks to prevent a crash at end. defaultIconViewThread()->stopAllTasks(); defaultThumbBarThread()->stopAllTasks(); defaultThread()->stopAllTasks(); } void ThumbnailLoadThread::initializeThumbnailDatabase(const DbEngineParameters& params, ThumbnailInfoProvider* const provider) { if (static_d->firstThreadCreated) { qCDebug(DIGIKAM_GENERAL_LOG) << "Call initializeThumbnailDatabase at application start. " "There are already thumbnail loading threads created, " "and these will not be switched to use the database. "; } ThumbsDbAccess::setParameters(params); if (ThumbsDbAccess::checkReadyForUse(nullptr)) { qCDebug(DIGIKAM_GENERAL_LOG) << "Thumbnails database ready for use"; static_d->storageMethod = ThumbnailCreator::ThumbnailDatabase; static_d->provider = provider; } else { QMessageBox::information(qApp->activeWindow(), i18n("Failed to initialize thumbnails database"), i18n("Error message: %1", ThumbsDbAccess().lastError())); } } void ThumbnailLoadThread::setDisplayingWidget(QWidget* const widget) { static_d->profile = IccManager::displayProfile(widget); } void ThumbnailLoadThread::setThumbnailSize(int size, bool forFace) { d->size = size; if (forFace) { d->creator->setThumbnailSize(size); } } int ThumbnailLoadThread::maximumThumbnailSize() { return ThumbnailSize::maxThumbsSize(); } int ThumbnailLoadThread::maximumThumbnailPixmapSize(bool highlight) { if (highlight) { return ThumbnailSize::maxThumbsSize(); } else { return ThumbnailSize::maxThumbsSize() + 2; // see slotThumbnailLoaded } } void ThumbnailLoadThread::setSendSurrogatePixmap(bool send) { d->sendSurrogate = send; } void ThumbnailLoadThread::setPixmapRequested(bool wantPixmap) { d->wantPixmap = wantPixmap; } void ThumbnailLoadThread::setHighlightPixmap(bool highlight) { d->highlight = highlight; } ThumbnailCreator* ThumbnailLoadThread::thumbnailCreator() const { return d->creator; } int ThumbnailLoadThread::thumbnailToPixmapSize(int size) const { return d->pixmapSizeForThumbnailSize(size); } int ThumbnailLoadThread::thumbnailToPixmapSize(bool withHighlight, int size) { if (withHighlight && size >= 10) { return size + 2; } return size; } int ThumbnailLoadThread::pixmapToThumbnailSize(int size) const { return d->thumbnailSizeForPixmapSize(size); } -bool ThumbnailLoadThread::Private::hasHighlightingBorder() const -{ - return highlight && size >= 10; -} - -int ThumbnailLoadThread::Private::pixmapSizeForThumbnailSize(int thumbnailSize) const -{ - if (hasHighlightingBorder()) - { - return thumbnailSize + 2; - } - - return thumbnailSize; -} - -int ThumbnailLoadThread::Private::thumbnailSizeForPixmapSize(int pixmapSize) const -{ - // bug #206666: Do not cut off one-pixel line for highlighting border - if (hasHighlightingBorder()) - { - return pixmapSize - 2; - } - - return pixmapSize; -} - -// --- Creating loading descriptions --- - -LoadingDescription ThumbnailLoadThread::Private::createLoadingDescription(const ThumbnailIdentifier& identifier, int size, - bool setLastDescription) -{ - size = thumbnailSizeForPixmapSize(size); - - LoadingDescription description(identifier.filePath, PreviewSettings(), size, - LoadingDescription::NoColorConversion, - LoadingDescription::PreviewParameters::Thumbnail); - description.previewParameters.storageReference = identifier.id; - - if (IccSettings::instance()->useManagedPreviews()) - { - description.postProcessingParameters.colorManagement = LoadingDescription::ConvertForDisplay; - description.postProcessingParameters.setProfile(static_d->profile); - } - - if (setLastDescription) - { - lastDescriptions.clear(); - lastDescriptions << description; - } - - return description; -} - -LoadingDescription ThumbnailLoadThread::Private::createLoadingDescription(const ThumbnailIdentifier& identifier, int size, - const QRect& detailRect, bool setLastDescription) -{ - size = thumbnailSizeForPixmapSize(size); - - LoadingDescription description(identifier.filePath, PreviewSettings(), size, - LoadingDescription::NoColorConversion, - LoadingDescription::PreviewParameters::DetailThumbnail); - description.previewParameters.storageReference = identifier.id; - - description.previewParameters.extraParameter = detailRect; - - if (IccSettings::instance()->useManagedPreviews()) - { - description.postProcessingParameters.colorManagement = LoadingDescription::ConvertForDisplay; - description.postProcessingParameters.setProfile(static_d->profile); - } - - if (setLastDescription) - { - lastDescriptions.clear(); - lastDescriptions << description; - } - - return description; -} - -bool ThumbnailLoadThread::Private::checkDescription(const LoadingDescription& description) -{ - QString cacheKey = description.cacheKey(); - - { - LoadingCache* const cache = LoadingCache::cache(); - LoadingCache::CacheLock lock(cache); - - if (cache->hasThumbnailPixmap(cacheKey)) - { - return false; - } - } - - { - QMutexLocker lock(&resultsMutex); - - if (collectedResults.contains(cacheKey)) - { - return false; - } - } - - return true; -} - -QList ThumbnailLoadThread::Private::makeDescriptions(const QList& identifiers, int size) -{ - QList descriptions; - { - LoadingDescription description = createLoadingDescription(ThumbnailIdentifier(), size, false); - - foreach (const ThumbnailIdentifier& identifier, identifiers) - { - description.filePath = identifier.filePath; - description.previewParameters.storageReference = identifier.id; - - if (!checkDescription(description)) - { - continue; - } - - descriptions << description; - } - } - - lastDescriptions = descriptions; - - return descriptions; -} - -QList ThumbnailLoadThread::Private::makeDescriptions(const QList >& identifiersAndRects, int size) -{ - QList descriptions; - { - LoadingDescription description = createLoadingDescription(ThumbnailIdentifier(), size, QRect(1,1,1,1), false); - typedef QPair IdRectPair; - - foreach (const IdRectPair& pair, identifiersAndRects) - { - description.filePath = pair.first.filePath; - description.previewParameters.storageReference = pair.first.id; - - if (!checkDescription(description)) - { - continue; - } - - description.previewParameters.extraParameter = pair.second; - descriptions << description; - } - } - - lastDescriptions = descriptions; - - return descriptions; -} bool ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, int size, QPixmap* retPixmap, bool emitSignal, const QRect& detailRect) { const QPixmap* pix = nullptr; LoadingDescription description; if (detailRect.isNull()) { description = d->createLoadingDescription(identifier, size); } else { description = d->createLoadingDescription(identifier, size, detailRect); } QString cacheKey = description.cacheKey(); { LoadingCache* const cache = LoadingCache::cache(); LoadingCache::CacheLock lock(cache); pix = cache->retrieveThumbnailPixmap(cacheKey); } if (pix) { if (retPixmap) { *retPixmap = *pix; } if (emitSignal) { load(description); emit signalThumbnailLoaded(description, QPixmap(*pix)); } return true; } { // If there is a result waiting for conversion to pixmap, return false - pixmap will come shortly QMutexLocker lock(&d->resultsMutex); if (d->collectedResults.contains(cacheKey)) { return false; } } load(description); return false; } // --- Normal thumbnails --- bool ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, QPixmap& retPixmap, int size) { return find(identifier, size, &retPixmap, false, QRect()); } bool ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, QPixmap& retPixmap) { return find(identifier, retPixmap, d->size); } void ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier) { find(identifier, d->size); } void ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, int size) { find(identifier, size, nullptr, true, QRect()); } void ThumbnailLoadThread::findGroup(QList& identifiers) { findGroup(identifiers, d->size); } void ThumbnailLoadThread::findGroup(QList& identifiers, int size) { if (!checkSize(size)) { return; } QList descriptions = d->makeDescriptions(identifiers, size); ManagedLoadSaveThread::prependThumbnailGroup(descriptions); } // --- Detail thumbnails --- bool ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, const QRect& rect, QPixmap& pixmap) { return find(identifier, rect, pixmap, d->size); } bool ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, const QRect& rect, QPixmap& pixmap, int size) { return find(identifier, size, &pixmap, false, rect); } void ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, const QRect& rect) { find(identifier, rect, d->size); } void ThumbnailLoadThread::find(const ThumbnailIdentifier& identifier, const QRect& rect, int size) { find(identifier, size, nullptr, true, rect); } void ThumbnailLoadThread::findGroup(const QList >& idsAndRects) { findGroup(idsAndRects, d->size); } void ThumbnailLoadThread::findGroup(const QList >& idsAndRects, int size) { if (!checkSize(size)) { return; } QList descriptions = d->makeDescriptions(idsAndRects, size); ManagedLoadSaveThread::prependThumbnailGroup(descriptions); } // --- Preloading --- void ThumbnailLoadThread::preload(const ThumbnailIdentifier& identifier) { preload(identifier, d->size); } void ThumbnailLoadThread::preload(const ThumbnailIdentifier& identifier, int size) { LoadingDescription description = d->createLoadingDescription(identifier, size); if (d->checkDescription(description)) { load(description, true); } } void ThumbnailLoadThread::preloadGroup(QList& identifiers) { preloadGroup(identifiers, d->size); } void ThumbnailLoadThread::preloadGroup(QList& identifiers, int size) { if (!checkSize(size)) { return; } QList descriptions = d->makeDescriptions(identifiers, size); ManagedLoadSaveThread::preloadThumbnailGroup(descriptions); } void ThumbnailLoadThread::pregenerateGroup(const QList& identifiers) { pregenerateGroup(identifiers, d->size); } void ThumbnailLoadThread::pregenerateGroup(const QList& identifiers, int size) { if (!checkSize(size)) { return; } QList descriptions = d->makeDescriptions(identifiers, size); for (int i = 0 ; i < descriptions.size() ; ++i) { descriptions[i].previewParameters.flags |= LoadingDescription::PreviewParameters::OnlyPregenerate; } ManagedLoadSaveThread::preloadThumbnailGroup(descriptions); } // --- Basic load() --- void ThumbnailLoadThread::load(const LoadingDescription& desc) { load(desc, false); } void ThumbnailLoadThread::load(const LoadingDescription& description, bool preload) { if (!checkSize(description.previewParameters.size)) { return; } if (preload) { ManagedLoadSaveThread::preloadThumbnail(description); } else { ManagedLoadSaveThread::loadThumbnail(description); } } QList ThumbnailLoadThread::lastDescriptions() const { return d->lastDescriptions; } bool ThumbnailLoadThread::checkSize(int size) { size = d->thumbnailSizeForPixmapSize(size); double ratio = qApp->devicePixelRatio(); int maxThumbSize = (ratio > 1.0) ? ThumbnailSize::MAX : ThumbnailSize::maxThumbsSize(); if (size <= 0) { qCDebug(DIGIKAM_GENERAL_LOG) << "ThumbnailLoadThread::load: No thumbnail size specified. Refusing to load thumbnail."; return false; } else if (size > maxThumbSize) { qCDebug(DIGIKAM_GENERAL_LOG) << "ThumbnailLoadThread::load: Thumbnail size " << size << " is larger than " << maxThumbSize << ". Refusing to load."; return false; } return true; } // --- Receiving --- // virtual method overridden from LoadSaveNotifier, implemented first by LoadSaveThread // called by ThumbnailTask from working thread void ThumbnailLoadThread::thumbnailLoaded(const LoadingDescription& loadingDescription, const QImage& img) { // call parent to send signalThumbnailLoaded(LoadingDescription, QImage) - signal is part of public API ManagedLoadSaveThread::thumbnailLoaded(loadingDescription, img); if (!d->wantPixmap) { return; } // Store result in our list and fire one signal // This means there can be several results per pixmap, // to speed up cases where inter-thread communication is the limiting factor QMutexLocker lock(&d->resultsMutex); d->collectedResults.insert(loadingDescription.cacheKey(), ThumbnailResult(loadingDescription, img)); // only sent signal when flag indicates there is no signal on the way currently if (!d->notifiedForResults) { d->notifiedForResults = true; emit thumbnailsAvailable(); } } void ThumbnailLoadThread::slotThumbnailsAvailable() { // harvest collected results safely into our thread QList results; { QMutexLocker lock(&d->resultsMutex); results = d->collectedResults.values(); d->collectedResults.clear(); // reset flag so that for next result, the signal is sent again d->notifiedForResults = false; } foreach (const ThumbnailResult& result, results) { slotThumbnailLoaded(result.loadingDescription, result.image); } } void ThumbnailLoadThread::slotThumbnailLoaded(const LoadingDescription& description, const QImage& thumb) { QPixmap pix; if (thumb.isNull()) { pix = surrogatePixmap(description); } else { int w = thumb.width(); int h = thumb.height(); // highlight only when requested and when thumbnail // width and height are greater than 10 if (d->highlight && (w >= 10 && h >= 10)) { pix = QPixmap(w + 2, h + 2); QPainter p(&pix); p.setPen(QPen(Qt::black, 1)); p.drawRect(0, 0, w + 1, h + 1); p.drawImage(1, 1, thumb); } else { pix = QPixmap::fromImage(thumb); } } // put into cache if (!pix.isNull()) { LoadingCache* const cache = LoadingCache::cache(); LoadingCache::CacheLock lock(cache); cache->putThumbnail(description.cacheKey(), pix, description.filePath); } emit signalThumbnailLoaded(description, pix); } QPixmap ThumbnailLoadThread::surrogatePixmap(const LoadingDescription& description) { if (!d->sendSurrogate) { return QPixmap(); } QPixmap pix; QMimeType mimeType = QMimeDatabase().mimeTypeForFile(description.filePath); if (mimeType.isValid()) { pix = QIcon::fromTheme(mimeType.genericIconName()).pixmap(128); } if (pix.isNull()) { pix = QIcon::fromTheme(QLatin1String("application-x-zerosize")).pixmap(128); } if (pix.isNull()) { // give up return QPixmap(); } // Resize icon to the right size depending of current settings. QSize size(pix.size()); size.scale(description.previewParameters.size, description.previewParameters.size, Qt::KeepAspectRatio); if (!pix.isNull() && size.width() < pix.width() && size.height() < pix.height()) { // only scale down // do not scale up, looks bad pix = pix.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } return pix; } // --- Utilities --- void ThumbnailLoadThread::storeDetailThumbnail(const QString& filePath, const QRect& detailRect, const QImage& image, bool isFace) { Q_UNUSED(isFace); d->creator->storeDetailThumbnail(filePath, detailRect, image); } int ThumbnailLoadThread::storedSize() const { return d->creator->storedSize(); } void ThumbnailLoadThread::deleteThumbnail(const QString& filePath) { { LoadingCache* const cache = LoadingCache::cache(); LoadingCache::CacheLock lock(cache); QStringList possibleKeys = LoadingDescription::possibleThumbnailCacheKeys(filePath); foreach (const QString& cacheKey, possibleKeys) { cache->removeThumbnail(cacheKey); } } ThumbnailCreator creator(static_d->storageMethod); if (static_d->provider) { creator.setThumbnailInfoProvider(static_d->provider); } creator.deleteThumbnailsFromDisk(filePath); } -// --- ThumbnailImageCatcher --------------------------------------------------------- - -class Q_DECL_HIDDEN ThumbnailImageCatcher::Private -{ - -public: - - enum CatcherState - { - Inactive, - Accepting, - Waiting, - Quitting - }; - -public: - - class Q_DECL_HIDDEN CatcherResult - { - public: - - explicit CatcherResult(const LoadingDescription& d) - : description(d), - received(false) - { - } - - CatcherResult(const LoadingDescription& d, const QImage& image) - : image(image), - description(d), - received(true) - { - } - - public: - - QImage image; - LoadingDescription description; - bool received; - }; - -public: - - explicit Private() - { - state = Inactive; - thread = nullptr; - active = true; - } - - void reset(); - void harvest(const LoadingDescription& description, const QImage& image); - -public: - - CatcherState state; - - bool active; - ThumbnailLoadThread* thread; - QList tasks; - QList intermediate; - - QMutex mutex; - QWaitCondition condVar; -}; - -void ThumbnailImageCatcher::Private::reset() -{ - intermediate.clear(); - tasks.clear(); - - if (active) - { - state = Accepting; - } - else - { - state = Inactive; - } -} - -void ThumbnailImageCatcher::Private::harvest(const LoadingDescription& description, const QImage& image) -{ - // called under lock - bool finished = true; - - for (int i = 0 ; i < tasks.size() ; ++i) - { - Private::CatcherResult& task = tasks[i]; - - if (task.description == description) - { - task.image = image; - task.received = true; - } - - finished = finished && task.received; - } - - if (finished) - { - state = Quitting; - condVar.wakeOne(); - } -} - ThumbnailImageCatcher::ThumbnailImageCatcher(QObject* const parent) : QObject(parent), d(new Private) { } ThumbnailImageCatcher::ThumbnailImageCatcher(ThumbnailLoadThread* const thread, QObject* const parent) : QObject(parent), d(new Private) { setThumbnailLoadThread(thread); } ThumbnailImageCatcher::~ThumbnailImageCatcher() { delete d; } ThumbnailLoadThread* ThumbnailImageCatcher::thread() const { return d->thread; } void ThumbnailImageCatcher::setThumbnailLoadThread(ThumbnailLoadThread* const thread) { if (d->thread == thread) { return; } d->state = Private::Inactive; if (d->thread) { disconnect(d->thread, SIGNAL(signalThumbnailLoaded(LoadingDescription,QImage)), this, SLOT(slotThumbnailLoaded(LoadingDescription,QImage))); } d->thread = thread; { QMutexLocker lock(&d->mutex); d->reset(); } if (d->thread) { connect(d->thread, SIGNAL(signalThumbnailLoaded(LoadingDescription,QImage)), this, SLOT(slotThumbnailLoaded(LoadingDescription,QImage)), Qt::DirectConnection); } } void ThumbnailImageCatcher::setActive(bool active) { if (d->active == active) { return; } if (!active) { cancel(); } QMutexLocker lock(&d->mutex); d->active = active; d->reset(); } void ThumbnailImageCatcher::cancel() { QMutexLocker lock(&d->mutex); if (d->state == Private::Waiting) { d->state = Private::Quitting; d->condVar.wakeOne(); } } void ThumbnailImageCatcher::slotThumbnailLoaded(const LoadingDescription& description, const QImage& image) { // We are in the thumbnail thread here, DirectConnection! QMutexLocker lock(&d->mutex); switch (d->state) { case Private::Inactive: break; case Private::Accepting: d->intermediate << Private::CatcherResult(description, image); break; case Private::Waiting: d->harvest(description, image); break; case Private::Quitting: break; } } int ThumbnailImageCatcher::enqueue() { QList descriptions = d->thread->lastDescriptions(); QMutexLocker lock(&d->mutex); foreach (const LoadingDescription& description, descriptions) { d->tasks << Private::CatcherResult(description); } return descriptions.size(); } QList ThumbnailImageCatcher::waitForThumbnails() { if (!d->thread || d->tasks.isEmpty() || !d->active) { return QList(); } QMutexLocker lock(&d->mutex); d->state = Private::Waiting; // first, handle results received between request and calling this method foreach (const Private::CatcherResult& result, d->intermediate) { d->harvest(result.description, result.image); } d->intermediate.clear(); // Now wait for the rest to arrive. If already finished, state will be Quitting while (d->state == Private::Waiting) { d->condVar.wait(&d->mutex); } QList result; foreach (const Private::CatcherResult& task, d->tasks) { result << task.image; } d->reset(); return result; } } // namespace Digikam diff --git a/core/libs/threadimageio/thumb/thumbnailloadthread_p.cpp b/core/libs/threadimageio/thumb/thumbnailloadthread_p.cpp new file mode 100644 index 0000000000..f33e7e7087 --- /dev/null +++ b/core/libs/threadimageio/thumb/thumbnailloadthread_p.cpp @@ -0,0 +1,175 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2007-06-05 + * Description : Thumbnail loading - private containers + * + * Copyright (C) 2006-2011 by Marcel Wiesweg + * Copyright (C) 2005-2019 by Gilles Caulier + * Copyright (C) 2015 by Mohamed_Anwer + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#include "thumbnailloadthread_p.h" + +namespace Digikam +{ + +bool ThumbnailLoadThread::Private::hasHighlightingBorder() const +{ + return highlight && size >= 10; +} + +int ThumbnailLoadThread::Private::pixmapSizeForThumbnailSize(int thumbnailSize) const +{ + if (hasHighlightingBorder()) + { + return thumbnailSize + 2; + } + + return thumbnailSize; +} + +int ThumbnailLoadThread::Private::thumbnailSizeForPixmapSize(int pixmapSize) const +{ + // bug #206666: Do not cut off one-pixel line for highlighting border + if (hasHighlightingBorder()) + { + return pixmapSize - 2; + } + + return pixmapSize; +} + +bool ThumbnailLoadThread::Private::checkDescription(const LoadingDescription& description) +{ + QString cacheKey = description.cacheKey(); + + { + LoadingCache* const cache = LoadingCache::cache(); + LoadingCache::CacheLock lock(cache); + + if (cache->hasThumbnailPixmap(cacheKey)) + { + return false; + } + } + + { + QMutexLocker lock(&resultsMutex); + + if (collectedResults.contains(cacheKey)) + { + return false; + } + } + + return true; +} + +QList ThumbnailLoadThread::Private::makeDescriptions(const QList& identifiers, int size) +{ + QList descriptions; + { + LoadingDescription description = createLoadingDescription(ThumbnailIdentifier(), size, false); + + foreach (const ThumbnailIdentifier& identifier, identifiers) + { + description.filePath = identifier.filePath; + description.previewParameters.storageReference = identifier.id; + + if (!checkDescription(description)) + { + continue; + } + + descriptions << description; + } + } + + lastDescriptions = descriptions; + + return descriptions; +} + +QList ThumbnailLoadThread::Private::makeDescriptions(const QList >& identifiersAndRects, int size) +{ + QList descriptions; + { + LoadingDescription description = createLoadingDescription(ThumbnailIdentifier(), size, QRect(1,1,1,1), false); + typedef QPair IdRectPair; + + foreach (const IdRectPair& pair, identifiersAndRects) + { + description.filePath = pair.first.filePath; + description.previewParameters.storageReference = pair.first.id; + + if (!checkDescription(description)) + { + continue; + } + + description.previewParameters.extraParameter = pair.second; + descriptions << description; + } + } + + lastDescriptions = descriptions; + + return descriptions; +} + +void ThumbnailImageCatcher::Private::reset() +{ + intermediate.clear(); + tasks.clear(); + + if (active) + { + state = Accepting; + } + else + { + state = Inactive; + } +} + +void ThumbnailImageCatcher::Private::harvest(const LoadingDescription& description, const QImage& image) +{ + // called under lock + bool finished = true; + + for (int i = 0 ; i < tasks.size() ; ++i) + { + Private::CatcherResult& task = tasks[i]; + + if (task.description == description) + { + task.image = image; + task.received = true; + } + + finished = finished && task.received; + } + + if (finished) + { + state = Quitting; + condVar.wakeOne(); + } +} + +} // namespace Digikam diff --git a/core/libs/threadimageio/thumb/thumbnailloadthread_p.h b/core/libs/threadimageio/thumb/thumbnailloadthread_p.h new file mode 100644 index 0000000000..f5198a4a53 --- /dev/null +++ b/core/libs/threadimageio/thumb/thumbnailloadthread_p.h @@ -0,0 +1,217 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2007-06-05 + * Description : Thumbnail loading - private containers + * + * Copyright (C) 2006-2011 by Marcel Wiesweg + * Copyright (C) 2005-2019 by Gilles Caulier + * Copyright (C) 2015 by Mohamed_Anwer + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#ifndef DIGIKAM_THUMB_NAIL_LOAD_THREAD_P_H +#define DIGIKAM_THUMB_NAIL_LOAD_THREAD_P_H + +#include "thumbnailloadthread.h" + +// Qt includes + +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes + +#include + +// Local includes + +#include "digikam_debug.h" +#include "dbengineparameters.h" +#include "iccmanager.h" +#include "iccprofile.h" +#include "iccsettings.h" +#include "metaenginesettings.h" +#include "thumbsdbaccess.h" +#include "thumbnailsize.h" +#include "thumbnailtask.h" +#include "thumbnailcreator.h" + +namespace Digikam +{ + +class Q_DECL_HIDDEN ThumbnailResult +{ + +public: + + explicit ThumbnailResult(const LoadingDescription& description, const QImage& image) + : loadingDescription(description), + image(image) + { + } + + LoadingDescription loadingDescription; + QImage image; +}; + +// ------------------------------------------------------------------- + +class Q_DECL_HIDDEN ThumbnailLoadThreadStaticPriv +{ +public: + + explicit ThumbnailLoadThreadStaticPriv() + : firstThreadCreated(false), + storageMethod(ThumbnailCreator::FreeDesktopStandard), + provider(nullptr), + profile(IccProfile::sRGB()) + { + } + + ~ThumbnailLoadThreadStaticPriv() + { + delete provider; + } + +public: + + bool firstThreadCreated; + + ThumbnailCreator::StorageMethod storageMethod; + ThumbnailInfoProvider* provider; + IccProfile profile; +}; + +// ------------------------------------------------------------------- + +class Q_DECL_HIDDEN ThumbnailLoadThread::Private +{ + +public: + + explicit Private() + { + size = ThumbnailSize::maxThumbsSize(); + wantPixmap = true; + highlight = true; + sendSurrogate = true; + notifiedForResults = false; + creator = nullptr; + } + + bool wantPixmap; + bool highlight; + bool sendSurrogate; + bool notifiedForResults; + + int size; + + ThumbnailCreator* creator; + + QHash collectedResults; + QMutex resultsMutex; + + QList lastDescriptions; + +public: + + LoadingDescription createLoadingDescription(const ThumbnailIdentifier& identifier, int size, bool setLastDescription = true); + LoadingDescription createLoadingDescription(const ThumbnailIdentifier& identifier, int size, + const QRect& detailRect, bool setLastDescription = true); + bool checkDescription(const LoadingDescription& description); + QList makeDescriptions(const QList& identifiers, int size); + QList makeDescriptions(const QList >& idsAndRects, int size); + bool hasHighlightingBorder() const; + int pixmapSizeForThumbnailSize(int thumbnailSize) const; + int thumbnailSizeForPixmapSize(int pixmapSize) const; +}; + +// --- ThumbnailImageCatcher --------------------------------------------------------- + +class Q_DECL_HIDDEN ThumbnailImageCatcher::Private +{ + +public: + + enum CatcherState + { + Inactive, + Accepting, + Waiting, + Quitting + }; + +public: + + class Q_DECL_HIDDEN CatcherResult + { + public: + + explicit CatcherResult(const LoadingDescription& d) + : description(d), + received(false) + { + } + + CatcherResult(const LoadingDescription& d, const QImage& image) + : image(image), + description(d), + received(true) + { + } + + public: + + QImage image; + LoadingDescription description; + bool received; + }; + +public: + + explicit Private() + { + state = Inactive; + thread = nullptr; + active = true; + } + + void reset(); + void harvest(const LoadingDescription& description, const QImage& image); + +public: + + CatcherState state; + + bool active; + ThumbnailLoadThread* thread; + QList tasks; + QList intermediate; + + QMutex mutex; + QWaitCondition condVar; +}; + +} // namespace Digikam + +#endif // DIGIKAM_THUMB_NAIL_LOAD_THREAD_P_H