File indexing completed on 2024-05-12 04:02:24
0001 /* -*- C++ -*- 0002 This file is part of ThreadWeaver. 0003 0004 SPDX-FileCopyrightText: 2005-2014 Mirko Boehm <mirko@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include <algorithm> //for transform 0010 #include <numeric> //for accumulate 0011 0012 #include <QDir> 0013 #include <QFileInfo> 0014 #include <QMutexLocker> 0015 #include <QStringList> 0016 #include <QtDebug> 0017 0018 #include <ThreadWeaver/DebuggingAids> 0019 #include <ThreadWeaver/Exception> 0020 #include <ThreadWeaver/ThreadWeaver> 0021 0022 #include "ComputeThumbNailJob.h" 0023 #include "FileLoaderJob.h" 0024 #include "ImageLoaderJob.h" 0025 #include "Model.h" 0026 #include "PriorityDecorator.h" 0027 0028 using namespace std; 0029 using namespace ThreadWeaver; 0030 0031 Model::Model(QObject *parent) 0032 : QAbstractListModel(parent) 0033 , m_fileLoaderRestriction(4) 0034 , m_imageLoaderRestriction(4) 0035 , m_imageScalerRestriction(4) 0036 , m_fileWriterRestriction(4) 0037 { 0038 ThreadWeaver::setDebugLevel(true, 0); 0039 connect(this, SIGNAL(signalElementChanged(int)), this, SLOT(slotElementChanged(int))); 0040 } 0041 0042 int Model::fileLoaderCap() const 0043 { 0044 return m_fileLoaderRestriction.cap(); 0045 } 0046 0047 void Model::setFileLoaderCap(int cap) 0048 { 0049 m_fileLoaderRestriction.setCap(cap); 0050 Queue::instance()->reschedule(); 0051 } 0052 0053 int Model::imageLoaderCap() const 0054 { 0055 return m_imageLoaderRestriction.cap(); 0056 } 0057 0058 void Model::setImageLoaderCap(int cap) 0059 { 0060 m_imageLoaderRestriction.setCap(cap); 0061 Queue::instance()->reschedule(); 0062 } 0063 0064 int Model::computeThumbNailCap() const 0065 { 0066 return m_imageScalerRestriction.cap(); 0067 } 0068 0069 void Model::setComputeThumbNailCap(int cap) 0070 { 0071 m_imageScalerRestriction.setCap(cap); 0072 } 0073 0074 int Model::saveThumbNailCap() const 0075 { 0076 return m_fileWriterRestriction.cap(); 0077 } 0078 0079 void Model::setSaveThumbNailCap(int cap) 0080 { 0081 m_imageScalerRestriction.setCap(cap); 0082 } 0083 0084 void Model::clear() 0085 { 0086 beginResetModel(); 0087 m_images.clear(); 0088 endResetModel(); 0089 } 0090 0091 void Model::prepareConversions(const QFileInfoList &filenames, const QString &outputDirectory) 0092 { 0093 beginResetModel(); 0094 Q_ASSERT(m_images.isEmpty()); 0095 m_images.reserve(filenames.size()); 0096 int counter = 0; 0097 auto initializeImage = [this, outputDirectory, &counter](const QFileInfo &file) { 0098 auto const out = QFileInfo(outputDirectory, file.fileName()).absoluteFilePath(); 0099 return Image(file.absoluteFilePath(), out, this, counter++); 0100 }; 0101 for (const auto &filename : filenames) { 0102 m_images << initializeImage(filename); 0103 } 0104 endResetModel(); 0105 } 0106 0107 bool Model::computeThumbNailsBlockingInLoop() 0108 { 0109 for (auto it = m_images.begin(); it != m_images.end(); ++it) { 0110 Image &image = *it; 0111 try { 0112 image.loadFile(); 0113 image.loadImage(); 0114 image.computeThumbNail(); 0115 image.saveThumbNail(); 0116 0117 } catch (const ThreadWeaver::Exception &ex) { 0118 qDebug() << ex.message(); 0119 return false; 0120 } 0121 } 0122 return true; 0123 } 0124 0125 bool Model::computeThumbNailsBlockingConcurrent() 0126 { 0127 auto queue = stream(); 0128 for (auto it = m_images.begin(); it != m_images.end(); ++it) { 0129 Image &image = *it; 0130 auto sequence = new Sequence(); 0131 *sequence << make_job([&image]() { 0132 image.loadFile(); 0133 }); 0134 *sequence << make_job([&image]() { 0135 image.loadImage(); 0136 }); 0137 *sequence << make_job([&image]() { 0138 image.computeThumbNail(); 0139 }); 0140 *sequence << make_job([&image]() { 0141 image.saveThumbNail(); 0142 }); 0143 queue << sequence; 0144 } 0145 queue.flush(); 0146 Queue::instance()->finish(); 0147 // figure out result: 0148 for (const Image &image : std::as_const(m_images)) { 0149 if (image.progress().first != Image::Step_NumberOfSteps) { 0150 return false; 0151 } 0152 } 0153 return true; 0154 } 0155 0156 void Model::queueUpConversion(const QStringList &files, const QString &outputDirectory) 0157 { 0158 QFileInfoList fileInfos; 0159 transform(files.begin(), files.end(), back_inserter(fileInfos), [](const QString &file) { 0160 return QFileInfo(file); 0161 }); 0162 prepareConversions(fileInfos, outputDirectory); 0163 // FIXME duplicated code 0164 auto queue = stream(); 0165 for (auto it = m_images.begin(); it != m_images.end(); ++it) { 0166 Image &image = *it; 0167 auto saveThumbNail = [&image]() { 0168 image.saveThumbNail(); 0169 }; 0170 auto saveThumbNailJob = new Lambda<decltype(saveThumbNail)>(saveThumbNail); 0171 { 0172 QMutexLocker l(saveThumbNailJob->mutex()); 0173 saveThumbNailJob->assignQueuePolicy(&m_fileWriterRestriction); 0174 } 0175 0176 auto sequence = new Sequence(); 0177 /* clang-format off */ 0178 *sequence << new FileLoaderJob(&image, &m_fileLoaderRestriction) 0179 << new ImageLoaderJob(&image, &m_imageLoaderRestriction) 0180 << new ComputeThumbNailJob(&image, &m_imageScalerRestriction) 0181 << new PriorityDecorator(Image::Step_SaveThumbNail, saveThumbNailJob); 0182 /* clang-format on */ 0183 queue << sequence; 0184 } 0185 } 0186 0187 Progress Model::progress() const 0188 { 0189 auto sumItUp = [](const Progress &sum, const Image &image) { 0190 auto const values = image.progress(); 0191 return qMakePair(sum.first + values.first, sum.second + values.second); 0192 }; 0193 auto const soFar = accumulate(m_images.begin(), m_images.end(), Progress(), sumItUp); 0194 return soFar; 0195 } 0196 0197 void Model::progressChanged() 0198 { 0199 auto const p = progress(); 0200 Q_EMIT progressStepChanged(p.first, p.second); 0201 } 0202 0203 void Model::elementChanged(int id) 0204 { 0205 signalElementChanged(id); 0206 } 0207 0208 int Model::rowCount(const QModelIndex &parent) const 0209 { 0210 Q_UNUSED(parent); 0211 return m_images.size(); 0212 } 0213 0214 QVariant Model::data(const QModelIndex &index, int role) const 0215 { 0216 if (!index.isValid()) { 0217 return QVariant(); 0218 } 0219 if (index.row() < 0 || index.row() >= rowCount()) { 0220 return QVariant(); 0221 } 0222 const Image &image = m_images.at(index.row()); 0223 if (role == Qt::DisplayRole) { 0224 return image.description(); 0225 } else if (role == Role_SortRole) { 0226 return -image.processingOrder(); 0227 } else if (role == Role_ImageRole) { 0228 return QVariant::fromValue(&image); 0229 } else if (role == Role_StepRole) { 0230 return QVariant::fromValue(image.progress().first); 0231 } 0232 return QVariant(); 0233 } 0234 0235 QVariant Model::headerData(int section, Qt::Orientation orientation, int role) const 0236 { 0237 Q_UNUSED(section); 0238 Q_UNUSED(orientation); 0239 Q_UNUSED(role); 0240 return QVariant(); 0241 } 0242 0243 void Model::slotElementChanged(int id) 0244 { 0245 if (id >= 0 && id < m_images.count()) { 0246 auto const i = index(id, 0); 0247 Q_EMIT dataChanged(i, i); 0248 } 0249 } 0250 0251 #include "moc_Model.cpp"