File indexing completed on 2024-05-05 04:22:01

0001 // SPDX-FileCopyrightText: 2003-2020 The KPhotoAlbum Development Team
0002 // SPDX-FileCopyrightText: 2022 Johannes Zarl-Zierl <johannes@zarl-zierl.at>
0003 //
0004 // SPDX-License-Identifier: GPL-2.0-or-later
0005 
0006 #include "ThumbnailBuilder.h"
0007 
0008 #include "AsyncLoader.h"
0009 #include "PreloadRequest.h"
0010 
0011 #include <DB/ImageDB.h>
0012 #include <DB/ImageInfoPtr.h>
0013 #include <DB/OptimizedFileList.h>
0014 #include <MainWindow/StatusBar.h>
0015 #include <ThumbnailView/CellGeometry.h>
0016 #include <kpabase/Logging.h>
0017 #include <kpabase/SettingsData.h>
0018 #include <kpathumbnails/ThumbnailCache.h>
0019 
0020 #include <KLocalizedString>
0021 #include <QLoggingCategory>
0022 #include <QMessageBox>
0023 #include <QTimer>
0024 
0025 namespace
0026 {
0027 /**
0028  * @brief Thumbnail size for storage.
0029  * @see ThumbnailView::CellGeometry::preferredIconSize()
0030  * @return
0031  */
0032 QSize preferredThumbnailSize()
0033 {
0034     int width = Settings::SettingsData::instance()->thumbnailSize();
0035     return QSize(width, width);
0036 }
0037 }
0038 
0039 ImageManager::ThumbnailBuilder *ImageManager::ThumbnailBuilder::s_instance = nullptr;
0040 
0041 ImageManager::ThumbnailBuilder::ThumbnailBuilder(MainWindow::StatusBar *statusBar, QObject *parent, ThumbnailCache *thumbnailCache)
0042     : QObject(parent)
0043     , m_statusBar(statusBar)
0044     , m_thumbnailCache(thumbnailCache)
0045     , m_count(0)
0046     , m_expectedThumbnails(0)
0047     , m_isBuilding(false)
0048     , m_loadedCount(0)
0049     , m_preloadQueue(nullptr)
0050     , m_scout(nullptr)
0051 {
0052     connect(m_statusBar, &MainWindow::StatusBar::cancelRequest, this, &ThumbnailBuilder::cancelRequests);
0053     s_instance = this;
0054 
0055     m_startBuildTimer = new QTimer(this);
0056     m_startBuildTimer->setSingleShot(true);
0057     connect(m_startBuildTimer, &QTimer::timeout, this, &ThumbnailBuilder::doThumbnailBuild);
0058 }
0059 
0060 void ImageManager::ThumbnailBuilder::cancelRequests()
0061 {
0062     ImageManager::AsyncLoader::instance()->stop(this, ImageManager::StopAll);
0063     m_isBuilding = false;
0064     m_statusBar->setProgressBarVisible(false);
0065     m_startBuildTimer->stop();
0066 }
0067 
0068 void ImageManager::ThumbnailBuilder::terminateScout()
0069 {
0070     if (m_scout) {
0071         delete m_scout;
0072         m_scout = nullptr;
0073     }
0074     if (m_preloadQueue) {
0075         delete m_preloadQueue;
0076         m_preloadQueue = nullptr;
0077     }
0078 }
0079 
0080 void ImageManager::ThumbnailBuilder::pixmapLoaded(ImageManager::ImageRequest *request, const QImage & /*image*/)
0081 {
0082     const DB::FileName fileName = request->databaseFileName();
0083     const QSize fullSize = request->fullSize();
0084     DB::ImageInfoPtr info = DB::ImageDB::instance()->info(fileName);
0085 
0086     // We probably shouldn't do this at all, since the "full size"
0087     // of the request could be the size of the embedded thumbnail
0088     // or even a scaled-down such.  But if this hasn't been
0089     // set orrectly earlier, we have nothing else to go on.
0090     if (fullSize.width() != -1 && info->size().width() == -1) {
0091         info->setSize(fullSize);
0092     }
0093     m_loadedCount++;
0094     m_statusBar->setProgress(++m_count);
0095     if (m_count >= m_expectedThumbnails) {
0096         terminateScout();
0097     }
0098 }
0099 
0100 void ImageManager::ThumbnailBuilder::buildAll(ThumbnailBuildStart when)
0101 {
0102     QMessageBox msgBox;
0103     msgBox.setText(i18n("Building all thumbnails may take a long time."));
0104     msgBox.setInformativeText(i18n("Do you want to rebuild all of your thumbnails?"));
0105     msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
0106     msgBox.setDefaultButton(QMessageBox::No);
0107     int ret = msgBox.exec();
0108     if (ret == QMessageBox::Yes) {
0109         m_thumbnailCache->flush();
0110         scheduleThumbnailBuild(DB::ImageDB::instance()->files(), when);
0111     }
0112 }
0113 
0114 ImageManager::ThumbnailBuilder *ImageManager::ThumbnailBuilder::instance()
0115 {
0116     Q_ASSERT(s_instance);
0117     return s_instance;
0118 }
0119 
0120 ImageManager::ThumbnailBuilder::~ThumbnailBuilder()
0121 {
0122     terminateScout();
0123 }
0124 
0125 void ImageManager::ThumbnailBuilder::buildMissing()
0126 {
0127     const DB::FileNameList images = DB::ImageDB::instance()->files();
0128     DB::FileNameList needed;
0129     for (const DB::FileName &fileName : images) {
0130         if (!m_thumbnailCache->contains(fileName))
0131             needed.append(fileName);
0132     }
0133     scheduleThumbnailBuild(needed, StartDelayed);
0134 }
0135 
0136 void ImageManager::ThumbnailBuilder::scheduleThumbnailBuild(const DB::FileNameList &list, ThumbnailBuildStart when)
0137 {
0138     if (list.count() == 0)
0139         return;
0140 
0141     if (m_isBuilding)
0142         cancelRequests();
0143 
0144     DB::OptimizedFileList files(list);
0145     m_thumbnailsToBuild = files.optimizedDbFiles();
0146     m_startBuildTimer->start(when == StartNow ? 0 : 5000);
0147 }
0148 
0149 void ImageManager::ThumbnailBuilder::buildOneThumbnail(const DB::ImageInfoPtr &info)
0150 {
0151     ImageManager::ImageRequest *request
0152         = new ImageManager::PreloadRequest(info->fileName(),
0153                                            preferredThumbnailSize(), info->angle(),
0154                                            this, m_thumbnailCache);
0155     request->setIsThumbnailRequest(true);
0156     request->setPriority(ImageManager::BuildThumbnails);
0157     ImageManager::AsyncLoader::instance()->load(request);
0158 }
0159 
0160 void ImageManager::ThumbnailBuilder::doThumbnailBuild()
0161 {
0162     m_isBuilding = true;
0163     int numberOfThumbnailsToBuild = 0;
0164 
0165     terminateScout();
0166 
0167     m_count = 0;
0168     m_loadedCount = 0;
0169     m_preloadQueue = new DB::ImageScoutQueue;
0170     for (const DB::FileName &fileName : m_thumbnailsToBuild) {
0171         m_preloadQueue->enqueue(fileName);
0172     }
0173     qCDebug(ImageManagerLog) << "thumbnail builder starting scout";
0174     m_scout = new DB::ImageScout(*m_preloadQueue, m_loadedCount, Settings::SettingsData::instance()->getThumbnailPreloadThreadCount());
0175     m_scout->setMaxSeekAhead(10);
0176     m_scout->setReadLimit(10 * 1048576);
0177     m_scout->start();
0178     m_statusBar->startProgress(i18n("Building thumbnails"), qMax(m_thumbnailsToBuild.size() - 1, 1));
0179     // We'll update this later.  Meanwhile, we want to make sure that the scout
0180     // isn't prematurely terminated because the expected number of thumbnails
0181     // is less than (i. e. zero) the number of thumbnails actually built.
0182     m_expectedThumbnails = m_thumbnailsToBuild.size();
0183     for (const DB::FileName &fileName : m_thumbnailsToBuild) {
0184         const auto info = DB::ImageDB::instance()->info(fileName);
0185         if (ImageManager::AsyncLoader::instance()->isExiting()) {
0186             cancelRequests();
0187             break;
0188         }
0189         if (info->isNull()) {
0190             m_loadedCount++;
0191             m_count++;
0192             continue;
0193         }
0194 
0195         ImageManager::ImageRequest *request
0196             = new ImageManager::PreloadRequest(fileName,
0197                                                preferredThumbnailSize(), info->angle(),
0198                                                this, m_thumbnailCache);
0199         request->setIsThumbnailRequest(true);
0200         request->setPriority(ImageManager::BuildThumbnails);
0201         if (ImageManager::AsyncLoader::instance()->load(request))
0202             ++numberOfThumbnailsToBuild;
0203     }
0204     m_expectedThumbnails = numberOfThumbnailsToBuild;
0205     if (numberOfThumbnailsToBuild == 0) {
0206         m_statusBar->setProgressBarVisible(false);
0207         terminateScout();
0208     }
0209 }
0210 
0211 void ImageManager::ThumbnailBuilder::save()
0212 {
0213     m_thumbnailCache->save();
0214 }
0215 
0216 void ImageManager::ThumbnailBuilder::requestCanceled()
0217 {
0218     m_statusBar->setProgress(++m_count);
0219     m_loadedCount++;
0220     if (m_count >= m_expectedThumbnails) {
0221         terminateScout();
0222     }
0223 }
0224 
0225 // vi:expandtab:tabstop=4 shiftwidth=4:
0226 
0227 #include "moc_ThumbnailBuilder.cpp"