1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2005-05-25
7 * Description : threaded image filter class.
8 *
9 * Copyright (C) 2005-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
10 * Copyright (C) 2007-2012 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
11 *
12 * This program is free software; you can redistribute it
13 * and/or modify it under the terms of the GNU General
14 * Public License as published by the Free Software Foundation;
15 * either version 2, or (at your option)
16 * any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * ============================================================ */
24
25 #include "dimgthreadedfilter.h"
26
27 // Qt includes
28
29 #include <QObject>
30 #include <QDateTime>
31 #include <QThreadPool>
32
33 // Local includes
34
35 #include "digikam_debug.h"
36
37 namespace Digikam
38 {
39
DImgThreadedFilter(QObject * const parent,const QString & name)40 DImgThreadedFilter::DImgThreadedFilter(QObject* const parent, const QString& name)
41 : DynamicThread(parent),
42 m_version(1),
43 m_wasCancelled(false)
44 {
45 setOriginalImage(DImg());
46 setFilterName(name);
47
48 initMaster();
49 }
50
DImgThreadedFilter(DImg * const orgImage,QObject * const parent,const QString & name)51 DImgThreadedFilter::DImgThreadedFilter(DImg* const orgImage, QObject* const parent,
52 const QString& name)
53 : DynamicThread(parent),
54 m_version(1),
55 m_wasCancelled(false)
56 {
57 // remove meta data
58
59 setOriginalImage(orgImage->copyImageData());
60 setFilterName(name);
61
62 initMaster();
63 }
64
DImgThreadedFilter(DImgThreadedFilter * const master,const DImg & orgImage,const DImg & destImage,int progressBegin,int progressEnd,const QString & name)65 DImgThreadedFilter::DImgThreadedFilter(DImgThreadedFilter* const master, const DImg& orgImage,
66 const DImg& destImage, int progressBegin, int progressEnd,
67 const QString& name)
68 : m_version(1),
69 m_wasCancelled(false),
70 m_destImage(destImage)
71 {
72 setFilterName(name);
73 setOriginalImage(orgImage);
74
75 initSlave(master, progressBegin, progressEnd);
76 }
77
~DImgThreadedFilter()78 DImgThreadedFilter::~DImgThreadedFilter()
79 {
80 // NOTE: use dynamic binding as this virtual method can be re-implemented in derived classes.
81
82 this->cancelFilter();
83
84 if (m_master)
85 {
86 m_master->setSlave(nullptr);
87 }
88 }
89
initSlave(DImgThreadedFilter * const master,int progressBegin,int progressEnd)90 void DImgThreadedFilter::initSlave(DImgThreadedFilter* const master, int progressBegin, int progressEnd)
91 {
92 m_master = master;
93 m_slave = nullptr;
94 m_progressBegin = progressBegin;
95 m_progressSpan = progressEnd - progressBegin;
96 m_progressCurrent = 0;
97
98 if (m_master)
99 {
100 m_master->setSlave(this);
101 }
102 }
103
initMaster()104 void DImgThreadedFilter::initMaster()
105 {
106 m_master = nullptr;
107 m_slave = nullptr;
108 m_progressBegin = 0;
109 m_progressSpan = 100;
110 m_progressCurrent = 0;
111 }
112
setupFilter(const DImg & orgImage)113 void DImgThreadedFilter::setupFilter(const DImg& orgImage)
114 {
115 setOriginalImage(orgImage);
116
117 // some filters may require that initFilter is called
118
119 initFilter();
120 }
121
setupAndStartDirectly(const DImg & orgImage,DImgThreadedFilter * const master,int progressBegin,int progressEnd)122 void DImgThreadedFilter::setupAndStartDirectly(const DImg& orgImage, DImgThreadedFilter* const master,
123 int progressBegin, int progressEnd)
124 {
125 initSlave(master, progressBegin, progressEnd);
126 setupFilter(orgImage);
127 }
128
setOriginalImage(const DImg & orgImage)129 void DImgThreadedFilter::setOriginalImage(const DImg& orgImage)
130 {
131 m_orgImage = orgImage;
132 }
133
setFilterName(const QString & name)134 void DImgThreadedFilter::setFilterName(const QString& name)
135 {
136 m_name = QString(name);
137 }
138
supportedVersions() const139 QList<int> DImgThreadedFilter::supportedVersions() const
140 {
141 return (QList<int>() << 1);
142 }
143
setFilterVersion(int version)144 void DImgThreadedFilter::setFilterVersion(int version)
145 {
146 if (supportedVersions().contains(version))
147 {
148 m_version = version;
149 }
150 }
151
filterVersion() const152 int DImgThreadedFilter::filterVersion() const
153 {
154 return m_version;
155 }
156
initFilter()157 void DImgThreadedFilter::initFilter()
158 {
159 prepareDestImage();
160
161 if (m_master)
162 {
163 startFilterDirectly();
164 }
165 }
166
prepareDestImage()167 void DImgThreadedFilter::prepareDestImage()
168 {
169 m_destImage.reset();
170
171 if (!m_orgImage.isNull())
172 {
173 m_destImage = DImg(m_orgImage.width(), m_orgImage.height(),
174 m_orgImage.sixteenBit(), m_orgImage.hasAlpha());
175 }
176 }
177
startFilter()178 void DImgThreadedFilter::startFilter()
179 {
180 if (m_orgImage.width() && m_orgImage.height())
181 {
182 start();
183 }
184 else // No image data
185 {
186 emit finished(false);
187 qCDebug(DIGIKAM_DIMG_LOG) << m_name << "::No valid image data !!! ...";
188 }
189 }
190
startFilterDirectly()191 void DImgThreadedFilter::startFilterDirectly()
192 {
193 if (m_orgImage.width() && m_orgImage.height())
194 {
195 emit started();
196
197 m_wasCancelled = false;
198
199 try
200 {
201 QDateTime now = QDateTime::currentDateTime();
202 filterImage();
203 //qCDebug(DIGIKAM_DIMG_LOG) << m_name << ":: excecution time : " << now.msecsTo(QDateTime::currentDateTime()) << " ms";
204 }
205 catch (std::bad_alloc& ex)
206 {
207 // TODO: User notification
208 qCCritical(DIGIKAM_DIMG_LOG) << "Caught out-of-memory exception! Aborting operation" << ex.what();
209
210 emit finished(false);
211
212 return;
213 }
214
215 emit finished(!m_wasCancelled);
216 }
217 else // No image data
218 {
219 emit finished(false);
220 qCDebug(DIGIKAM_DIMG_LOG) << m_name << "::No valid image data !!! ...";
221 }
222 }
223
run()224 void DImgThreadedFilter::run()
225 {
226 startFilterDirectly();
227 }
228
cancelFilter()229 void DImgThreadedFilter::cancelFilter()
230 {
231 if (isRunning())
232 {
233 m_wasCancelled = true;
234 }
235
236 stop();
237
238 if (m_slave)
239 {
240 m_slave->stop();
241
242 // do not wait on slave, it is not running in its own separate thread!
243 //m_slave->cleanupFilter();
244 }
245
246 wait();
247 cleanupFilter();
248 }
249
postProgress(int progr)250 void DImgThreadedFilter::postProgress(int progr)
251 {
252 if (m_master)
253 {
254 progr = modulateProgress(progr);
255 m_master->postProgress(progr);
256 }
257 else if (m_progressCurrent != progr)
258 {
259 emit progress(progr);
260 m_progressCurrent = progr;
261 }
262 }
263
setSlave(DImgThreadedFilter * const slave)264 void DImgThreadedFilter::setSlave(DImgThreadedFilter* const slave)
265 {
266 m_slave = slave;
267 }
268
modulateProgress(int progress)269 int DImgThreadedFilter::modulateProgress(int progress)
270 {
271 return m_progressBegin + (int)((double)progress * (double)m_progressSpan / 100.0);
272 }
273
parametersSuccessfullyRead() const274 bool DImgThreadedFilter::parametersSuccessfullyRead() const
275 {
276 return true;
277 }
278
readParametersError(const FilterAction &) const279 QString DImgThreadedFilter::readParametersError(const FilterAction&) const
280 {
281 return QString();
282 }
283
multithreadedSteps(int stop,int start) const284 QList<int> DImgThreadedFilter::multithreadedSteps(int stop, int start) const
285 {
286 uint nbCore = QThreadPool::globalInstance()->maxThreadCount();
287 float step = ((float)stop - (float)start) / (float)nbCore;
288 QList<int> vals;
289
290 vals << start;
291
292 for (uint i = 1 ; i < nbCore ; ++i)
293 {
294 vals << vals.last() + step;
295 }
296
297 vals << stop;
298
299 return vals;
300 }
301
302 } // namespace Digikam
303