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 #ifndef DIGIKAM_DIMG_THREADED_FILTER_H
26 #define DIGIKAM_DIMG_THREADED_FILTER_H
27 
28 // Local includes
29 
30 #include "digikam_export.h"
31 #include "dimg.h"
32 #include "dynamicthread.h"
33 #include "filteraction.h"
34 
35 class QObject;
36 
37 namespace Digikam
38 {
39 
40 class DIGIKAM_EXPORT DImgThreadedFilter : public DynamicThread
41 {
42     Q_OBJECT
43 
44 public:
45 
46     /**
47      * Constructs a filter without argument.
48      * You need to call setupFilter() and startFilter()
49      * to start the threaded computation.
50      * To run filter without to use multithreading, call startFilterDirectly().
51      */
52     explicit DImgThreadedFilter(QObject* const parent = nullptr, const QString& name = QString());
53 
54     /**
55      * Constructs a filter with all arguments (ready to use).
56      * The given original image will be copied.
57      * You need to call startFilter() to start the threaded computation.
58      * To run filter without to use multithreading, call startFilterDirectly().
59      */
60     DImgThreadedFilter(DImg* const orgImage,
61                        QObject* const parent,
62                        const QString& name = QString());
63 
64     ~DImgThreadedFilter() override;
65 
66     /**
67      * You need to call this and then start filter of you used
68      * the constructor not setting an original image.
69      * The original image's data will not be copied.
70      */
71     void setupFilter(const DImg& orgImage);
72 
73     /**
74      * Initializes the filter for use as a slave and directly starts computation (in-thread)
75      */
76     void setupAndStartDirectly(const DImg& orgImage,
77                                DImgThreadedFilter* const master,
78                                int progressBegin = 0,
79                                int progressEnd = 100);
80 
81     void setOriginalImage(const DImg& orgImage);
82     void setFilterName(const QString& name);
83 
getTargetImage()84     DImg getTargetImage()
85     {
86         return m_destImage;
87     };
88 
filterName()89     const QString& filterName()
90     {
91         return m_name;
92     };
93 
94     /**
95      * This method return a list of steps to process parallelized operation in filter using QtConcurrents API.
96      * Usually, start and stop are rows or columns from image to process. By default, whole image will be processed
97      * and start value is 0. In this case stop will be last row or column to process.
98      * Between range [start,stop], this method will divide by equal steps depending of number of CPU cores available.
99      * To be sure that all values will be processed, in case of CPU core division give rest, the last step compensate
100      * the difference.
101      * See Blur filter loop implementation for example to see how to use this method with QtConcurrents API.
102      */
103     QList<int> multithreadedSteps(int stop, int start = 0)                      const;
104 
105     /**
106      * Start the threaded computation.
107      */
108     virtual void startFilter();
109 
110     /**
111      * Cancel the threaded computation.
112      */
113     virtual void cancelFilter();
114 
115     /**
116      * Start computation of this filter, directly in this thread.
117      */
118     virtual void startFilterDirectly();
119 
120     /**
121      * Returns the action description corresponding to currently set options.
122      */
123     virtual FilterAction filterAction()                                               = 0;
124 
125     virtual void readParameters(const FilterAction&)                                  = 0;
126 
127     /**
128      * Return the identifier for this filter in the image history.
129      */
130     virtual QString filterIdentifier()                                          const = 0;
131 
132     virtual QList<int> supportedVersions()                                      const;
133 
134     /**
135      *  Replaying a filter action:
136      *  Set the filter version. A filter may implement different versions, to preserve
137      *  image history when the algorithm is changed.
138      *  Any value set here must be contained in supportedVersions, otherwise
139      *  this call will be ignored. Default value is 1.
140      *  (Note: If you intend to _record_ a filter action, please look at FilterAction's m_version)
141      */
142     void setFilterVersion(int version);
143     int filterVersion()                                                         const;
144 
145     /**
146      * Optional: error handling for readParameters.
147      * When readParameters() has been called, this method will return true
148      * if the call was successful, and false if not.
149      * If returning false, readParametersError() will give an error message.
150      * The default implementation always returns success. You only need to reimplement
151      * when a filter is likely to fail in a different environment, e.g.
152      * depending on availability of installed files.
153      * These methods have an undefined return value if readParameters() was not called
154      * previously.
155      */
156     virtual bool parametersSuccessfullyRead()                                   const;
157     virtual QString readParametersError(const FilterAction& actionThatFailed)   const;
158 
159 Q_SIGNALS:
160 
161     /**
162      * This signal is emitted when image data is available and the computation has started.
163      */
164     void started();
165 
166     /**
167      * Emitted when progress info from the calculation is available.
168      */
169     void progress(int progress);
170 
171     /**
172      * Emitted when the computation has completed.
173      * @param success True if computation finished without interruption on valid data
174      *                False if the thread was canceled, or no data is available.
175      */
176     void finished(bool success);
177 
178 protected:
179 
180     /**
181      * Start filter operation before threaded method. Must be called by your constructor.
182      */
183     virtual void initFilter();
184 
185     /**
186      * List of threaded operations by filter.
187      */
188     void run() override;
189 
190     /**
191      * Main image filter method. Override in subclass.
192      */
193     virtual void filterImage() = 0;
194 
195     /**
196      * Clean up filter data if necessary, called by stopComputation() method.
197      * Override in subclass.
198      */
cleanupFilter()199     virtual void cleanupFilter() {};
200 
201     /**
202      * Emit progress info.
203      */
204     void postProgress(int progress);
205 
206 protected:
207 
208     /**
209      * Support for chaining two filters as master and thread.
210      *
211      * Do not call startFilter() or startFilterDirectly() on this.
212      * The computation will be started from initFilter() which you must
213      * call from the derived class constructor.
214      *
215      * Constructor for slave mode:
216      * Constructs a new slave filter with the specified master.
217      * The filter will be executed in the current thread.
218      * orgImage and destImage will not be copied.
219      * Note that the slave is still free to reallocate his destImage.
220      * progressBegin and progressEnd can indicate the progress span
221      * that the slave filter uses in the parent filter's progress.
222      * Any derived filter class that is publicly available to other filters
223      * should implement an additional constructor using this constructor.
224      */
225     DImgThreadedFilter(DImgThreadedFilter* const master,
226                        const DImg& orgImage,
227                        const DImg& destImage,
228                        int progressBegin = 0,
229                        int progressEnd = 100,
230                        const QString& name = QString());
231 
232     /**
233      * Initialize the filter for use as a slave - reroutes progress info to master.
234      * Note: Computation will be started from setupFilter().
235      */
236     void initSlave(DImgThreadedFilter* const master,
237                    int progressBegin = 0,
238                    int progressEnd = 100);
239 
240     /**
241      * Inform the master that there is currently a slave. At destruction of the slave, call with slave=0.
242      */
243     void setSlave(DImgThreadedFilter* const slave);
244 
245     /**
246      * This method modulates the progress value from the 0..100 span to the span of this slave.
247      * Called by postProgress if master is not null.
248      */
249     virtual int modulateProgress(int progress);
250 
251     void initMaster();
252     virtual void prepareDestImage();
253 
254     /**
255      * Convenience class to spare the few repeating lines of code
256      */
257     template <class Filter>
258 
259     class DefaultFilterAction : public FilterAction
260     {
261     public:
262 
263         explicit DefaultFilterAction(FilterAction::Category category = FilterAction::ReproducibleFilter)
FilterAction(Filter::FilterIdentifier (),Filter::CurrentVersion (),category)264             : FilterAction(Filter::FilterIdentifier(), Filter::CurrentVersion(), category)
265         {
266             setDisplayableName(Filter::DisplayableName());
267         }
268 
DefaultFilterAction(bool isReproducible)269         explicit DefaultFilterAction(bool isReproducible)
270             : FilterAction(Filter::FilterIdentifier(), Filter::CurrentVersion(),
271                            isReproducible ? FilterAction::ReproducibleFilter : FilterAction::ComplexFilter)
272         {
273             setDisplayableName(Filter::DisplayableName());
274         }
275 
276         /**
277          * Preserve backwards compatibility:
278          * If a given condition (some new feature is not used) is true,
279          * decrease the version so that older digikam versions can still replay the action
280          */
supportOlderVersionIf(int version,bool condition)281         void supportOlderVersionIf(int version, bool condition)
282         {
283             if (condition && (version <= m_version))
284             {
285                 m_version = version;
286             }
287         }
288 
289     };
290 
291 protected:
292 
293     int                 m_version;
294 
295     bool                m_wasCancelled;
296 
297     /**
298      * The progress span that a slave filter uses in the parent filter's progress.
299      */
300     int                 m_progressBegin;
301     int                 m_progressSpan;
302     int                 m_progressCurrent;  ///< To prevent signals bombarding with progress indicator value in postProgress().
303 
304     /**
305      * Filter name.
306      */
307     QString             m_name;
308 
309     /**
310      * Copy of original Image data.
311      */
312     DImg                m_orgImage;
313 
314     /**
315      * Output image data.
316      */
317     DImg                m_destImage;
318 
319     /**
320      * The current slave. Any filter might want to use another filter while processing.
321      */
322     DImgThreadedFilter* m_slave;
323 
324     /**
325      * The master of this slave filter. Progress info will be routed to this one.
326      */
327     DImgThreadedFilter* m_master;
328 };
329 
330 } // namespace Digikam
331 
332 #endif // DIGIKAM_DIMG_THREADED_FILTER_H
333