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,
73 const QImage &mask,
74 const QVariant &parameters,
75 const AsyncImageRenderCallback::InterlacingState state) override;
76 void startRenderingAsync(const QVariant &parameters);
77 [[nodiscard]] virtual bool shouldAbort() const override;
78 void waitForIdle();
79
81 /** @brief Result of an interlacing pass of the <em>rendering</em>
82 * operation.
83 *
84 * <em>Rendering</em> operations can be started
85 * by @ref startRenderingAsync().
86 *
87 * @note <em>Rendering</em> operations might be stopped before emitting
88 * this signal by calling again @ref startRenderingAsync(); therefore it
89 * is <em>not</em> guaranteed that each call of @ref startRenderingAsync()
90 * will finally emit this signal.
91 *
92 * @param image The image
93 * @param mask The alpha mask, if provided. Renderers may choose whether
94 * to supply an alpha mask. Alpha masks are 1-bit images where white
95 * represents transparency and black represents opacity, defining the
96 * transparency state <i>before</i> any anti-aliasing is applied. This
97 * differs from the potentially anti-aliased image itself, which may
98 * contain partial transparency, making it difficult to determine the
99 * original transparency before anti-aliasing. Typically, fully transparent
100 * pixels will have an alpha value greater than 50% after anti-aliasing,
101 * but in some cases, they may fall below this threshold. The alpha mask,
102 * however, provides a clear and definitive indication of each pixel’s
103 * validity.
104 * @param parameters The parameters of the image
105 * @param state The interlacing state of the image. A render function
106 * must first return zero or more images with intermediate state. After
107 * that, it must return exactly one image with final state (unless it
108 * was aborted). After that, it must not return any more images.
109 *
110 * @warning This signal can be emitted by a thread other than the
111 * thread in which this object itself lives. Therefore, use only
112 * <tt>Qt::AutoConnection</tt> or <tt>Qt::QueuedConnection</tt>
113 * when connecting to this signal. */
114 void interlacingPassCompleted(const QImage &image,
115 const QImage &mask,
116 const QVariant &parameters,
117 const PerceptualColor::AsyncImageRenderCallback::InterlacingState state);
118
119protected:
120 virtual void run() override;
121
122private:
123 Q_DISABLE_COPY(AsyncImageRenderThread)
124
125 /** @internal @brief Only for unit tests. */
126 friend class TestAsyncImageRenderThread;
127
128 /** @brief Provide parameters for the next re(start) of @ref run().
129 *
130 * @ref run() is supposed to read these parameters on each round,
131 * and to render a corresponding image.
132 *
133 * @note This data member has read and write access protected
134 * by @ref m_loopMutex. */
135 QVariant m_imageParameters;
136 /** @brief Request @ref run() to abort.
137 *
138 * @ref run() is supposed to control regularly if this value
139 * is <tt>true</tt>. If so, it should return as fast as possible.
140 * This variable is used by the destructor to make sure that the
141 * associated thread is stopped before destroying this object.
142 *
143 * @warning This is used with @ref m_loopCondition. See there for details.
144 *
145 * @note This data member has write access protected
146 * by @ref m_loopMutex. */
147 std::atomic_bool m_loopAbort = false;
148 /** @brief Wait condition used between the rendering rounds.
149 *
150 * @warning @ref m_loopAbort and @ref m_loopRestart are used to control the
151 * waiting. Changing them requires locking @ref m_loopMutex (otherwise,
152 * this condition could become out-of-synchronization). Reading them
153 * during the rendering to stop more immediately should be okay, as
154 * both variables are atomic.
155 *
156 * @note See
157 * <a href="https://www.heise.de/developer/artikel/C-Core-Guidelines-Sei-dir-der-Fallen-von-Bedingungsvariablen-bewusst-4063822.html">
158 * this in-depth explication</a> or also
159 * <a href="https://www.grimm-jaud.de/index.php/blog/bedingungsvariablen">
160 * this other in-depth explication</a>, both of Rainer Grimm, for
161 * more details about this synchronization pattern. */
162 QWaitCondition m_loopCondition;
163 /** @brief Mutex protection for @ref m_loopAbort and @ref m_loopRestart
164 * and @ref m_imageParameters.
165 *
166 * @warning This is used with @ref m_loopCondition. See there for details. */
167 QMutex m_loopMutex;
168 /** @brief Request @ref run() to restart its outer loop.
169 *
170 * @ref run() is supposed to control regularly if this value is
171 * <tt>true</tt>. If so, it should restart its outer loop as fast as
172 * possible. This variable is set by @ref startRenderingAsync() to
173 * <tt>true</tt> to make sure that the outer loop restarts, and it is set
174 * by @ref run() to <tt>false</tt> once the restart of the outer loop
175 * has happened.
176 *
177 * @warning This is used with @ref m_loopCondition. See there for details.
178 *
179 * @note This data member has write access protected
180 * by @ref m_loopMutex. */
181 std::atomic_bool m_loopRestart = false;
182 /** @brief Function pointer to the function that does the
183 * actual rendering. */
184 const pointerToRenderFunction m_renderFunction;
185 /** @brief Wait condition to wait until this thread goes to sleep. */
186 QWaitCondition m_syncCondition;
187 /** @brief Is <tt>true</tt> if the render thread is either sleeping
188 * or not yet started at all. */
189 std::atomic_bool m_syncIsIdle = true;
190 /** @brief Mutex protection for @ref m_syncCondition */
191 QMutex m_syncMutex;
192};
193
194} // namespace PerceptualColor
195
196#endif // ASYNCIMAGERENDERTHREAD_H
The namespace of this library.
QObject(QObject *parent)
Q_OBJECTQ_OBJECT
Q_SIGNALSQ_SIGNALS
QObject * parent() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Apr 25 2025 12:03:13 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.