Perceptual Color

asyncimagerenderthread.h
1// SPDX-FileCopyrightText: Lukas Sommer <sommerluk@gmail.com>
2// SPDX-License-Identifier: BSD-2-Clause OR MIT
3
4#ifndef ASYNCIMAGERENDERTHREAD_H
5#define ASYNCIMAGERENDERTHREAD_H
6
7#include "asyncimagerendercallback.h"
8#include <atomic>
9#include <functional>
10#include <qglobal.h>
11#include <qmutex.h>
12#include <qthread.h>
13#include <qvariant.h>
14#include <qwaitcondition.h>
15
16#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
17#include <qtmetamacros.h>
18#else
19#include <qobjectdefs.h>
20#include <qstring.h>
21#endif
22
23class QImage;
24class QObject;
25
26namespace PerceptualColor
27{
28/** @internal
29 *
30 * @brief Provides threaded rendering for @ref AsyncImageProvider. */
31class AsyncImageRenderThread : public QThread, public AsyncImageRenderCallback
32{
34
35public:
36 /** @brief Function pointer to a render function.
37 *
38 * The function pointed to by this pointer has <tt>void</tt> as its
39 * return value. It has the following parameters:
40 *
41 * @param variantParameters A <tt>QVariant</tt> that contains the
42 * image parameters.
43 * @param callbackObject An object that provides the necessary
44 * callbacks.
45 *
46 * The function pointed to by this pointer is supposed to
47 * render the image with the given parameters, and deliver the
48 * result of each interlacing pass and also the final result by
49 * callbacks. It also is supposed to check regularly via callbacks
50 * if it should abort the rendering.
51 *
52 * The function pointed to by this pointer must be thread-safe.
53 *
54 * @internal
55 *
56 * The render function is meant to be used
57 * by @ref AsyncImageRenderThread.
58 *
59 * @note It might be possible to use <tt>
60 * <a href="https://en.cppreference.com/w/cpp/utility/any">std::any</a>
61 * </tt> instead of <tt><a href="https://doc.qt.io/qt-6/qvariant.html">
62 * QVariant</a></tt>. This might eliminate the need to register
63 * types to the Qt meta-type system. On the other hand, it
64 * probably will not integrate as well with other parts of Qt
65 * (signals, slots…). So currently we are doing well by using <tt>
66 * <a href="https://doc.qt.io/qt-6/qvariant.html"> QVariant</a></tt>. */
67 using pointerToRenderFunction = std::function<void(const QVariant &variantParameters, AsyncImageRenderCallback &callbackObject)>;
68
69 explicit AsyncImageRenderThread(const pointerToRenderFunction &renderFunction, QObject *parent = nullptr);
70 virtual ~AsyncImageRenderThread() override;
71
72 virtual void deliverInterlacingPass(const QImage &image, const QVariant &parameters, const AsyncImageRenderCallback::InterlacingState state) override;
73 void startRenderingAsync(const QVariant &parameters);
74 [[nodiscard]] virtual bool shouldAbort() const override;
75 void waitForIdle();
76
78 /** @brief Result of an interlacing pass of the <em>rendering</em>
79 * operation.
80 *
81 * <em>Rendering</em> operations can be started
82 * by @ref startRenderingAsync().
83 *
84 * @note <em>Rendering</em> operations might be stopped before emitting
85 * this signal by calling again @ref startRenderingAsync(); therefore it
86 * is <em>not</em> guaranteed that each call of @ref startRenderingAsync()
87 * will finally emit this signal.
88 *
89 * @param image The image
90 * @param parameters The parameters of the image
91 * @param state The interlacing state of the image. A render function
92 * must first return zero or more images with intermediate state. After
93 * that, it must return exactly one image with final state (unless it
94 * was aborted). After that, it must not return any more images.
95 *
96 * @warning This signal can be emitted by a thread other than the
97 * thread in which this object itself lives. Therefore, use only
98 * <tt>Qt::AutoConnection</tt> or <tt>Qt::QueuedConnection</tt>
99 * when connecting to this signal. */
100 void interlacingPassCompleted(const QImage &image, const QVariant &parameters, const PerceptualColor::AsyncImageRenderCallback::InterlacingState state);
101
102protected:
103 virtual void run() override;
104
105private:
106 Q_DISABLE_COPY(AsyncImageRenderThread)
107
108 /** @internal @brief Only for unit tests. */
109 friend class TestAsyncImageRenderThread;
110
111 /** @brief Provide parameters for the next re(start) of @ref run().
112 *
113 * @ref run() is supposed to read these parameters on each round,
114 * and to render a corresponding image.
115 *
116 * @note This data member has read and write access protected
117 * by @ref m_loopMutex. */
118 QVariant m_imageParameters;
119 /** @brief Request @ref run() to abort.
120 *
121 * @ref run() is supposed to control regularly if this value
122 * is <tt>true</tt>. If so, it should return as fast as possible.
123 * This variable is used by the destructor to make sure that the
124 * associated thread is stopped before destroying this object.
125 *
126 * @warning This is used with @ref m_loopCondition. See there for details.
127 *
128 * @note This data member has write access protected
129 * by @ref m_loopMutex. */
130 std::atomic_bool m_loopAbort = false;
131 /** @brief Wait condition used between the rendering rounds.
132 *
133 * @warning @ref m_loopAbort and @ref m_loopRestart are used to control the
134 * waiting. Changing them requires locking @ref m_loopMutex (otherwise,
135 * this condition could become out-of-synchronization). Reading them
136 * during the rendering to stop more immediately should be okay, as
137 * both variables are atomic.
138 *
139 * @note See
140 * <a href="https://www.heise.de/developer/artikel/C-Core-Guidelines-Sei-dir-der-Fallen-von-Bedingungsvariablen-bewusst-4063822.html">
141 * this in-depth explication</a> or also
142 * <a href="https://www.grimm-jaud.de/index.php/blog/bedingungsvariablen">
143 * this other in-depth explication</a>, both of Rainer Grimm, for
144 * more details about this synchronization pattern. */
145 QWaitCondition m_loopCondition;
146 /** @brief Mutex protection for @ref m_loopAbort and @ref m_loopRestart
147 * and @ref m_imageParameters.
148 *
149 * @warning This is used with @ref m_loopCondition. See there for details. */
150 QMutex m_loopMutex;
151 /** @brief Request @ref run() to restart its outer loop.
152 *
153 * @ref run() is supposed to control regularly if this value is
154 * <tt>true</tt>. If so, it should restart its outer loop as fast as
155 * possible. This variable is set by @ref startRenderingAsync() to
156 * <tt>true</tt> to make sure that the outer loop restarts, and it is set
157 * by @ref run() to <tt>false</tt> once the restart of the outer loop
158 * has happened.
159 *
160 * @warning This is used with @ref m_loopCondition. See there for details.
161 *
162 * @note This data member has write access protected
163 * by @ref m_loopMutex. */
164 std::atomic_bool m_loopRestart = false;
165 /** @brief Function pointer to the function that does the
166 * actual rendering. */
167 const pointerToRenderFunction m_renderFunction;
168 /** @brief Wait condition to wait until this thread goes to sleep. */
169 QWaitCondition m_syncCondition;
170 /** @brief Is <tt>true</tt> if the render thread is either sleeping
171 * or not yet started at all. */
172 std::atomic_bool m_syncIsIdle = true;
173 /** @brief Mutex protection for @ref m_syncCondition */
174 QMutex m_syncMutex;
175};
176
177} // namespace PerceptualColor
178
179#endif // ASYNCIMAGERENDERTHREAD_H
The namespace of this library.
Q_OBJECTQ_OBJECT
Q_SIGNALSQ_SIGNALS
QObject * parent() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 24 2024 11:48:43 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.