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"