1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the examples of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:BSD$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** BSD License Usage |
18 | ** Alternatively, you may use this file under the terms of the BSD license |
19 | ** as follows: |
20 | ** |
21 | ** "Redistribution and use in source and binary forms, with or without |
22 | ** modification, are permitted provided that the following conditions are |
23 | ** met: |
24 | ** * Redistributions of source code must retain the above copyright |
25 | ** notice, this list of conditions and the following disclaimer. |
26 | ** * Redistributions in binary form must reproduce the above copyright |
27 | ** notice, this list of conditions and the following disclaimer in |
28 | ** the documentation and/or other materials provided with the |
29 | ** distribution. |
30 | ** * Neither the name of The Qt Company Ltd nor the names of its |
31 | ** contributors may be used to endorse or promote products derived |
32 | ** from this software without specific prior written permission. |
33 | ** |
34 | ** |
35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." |
46 | ** |
47 | ** $QT_END_LICENSE$ |
48 | ** |
49 | ****************************************************************************/ |
50 | |
51 | #include "renderthread.h" |
52 | |
53 | #include <QImage> |
54 | #include <cmath> |
55 | |
56 | //! [0] |
57 | RenderThread::RenderThread(QObject *parent) |
58 | : QThread(parent) |
59 | { |
60 | for (int i = 0; i < ColormapSize; ++i) |
61 | colormap[i] = rgbFromWaveLength(wave: 380.0 + (i * 400.0 / ColormapSize)); |
62 | } |
63 | //! [0] |
64 | |
65 | //! [1] |
66 | RenderThread::~RenderThread() |
67 | { |
68 | mutex.lock(); |
69 | abort = true; |
70 | condition.wakeOne(); |
71 | mutex.unlock(); |
72 | |
73 | wait(); |
74 | } |
75 | //! [1] |
76 | |
77 | //! [2] |
78 | void RenderThread::render(double centerX, double centerY, double scaleFactor, |
79 | QSize resultSize, double devicePixelRatio) |
80 | { |
81 | QMutexLocker locker(&mutex); |
82 | |
83 | this->centerX = centerX; |
84 | this->centerY = centerY; |
85 | this->scaleFactor = scaleFactor; |
86 | this->devicePixelRatio = devicePixelRatio; |
87 | this->resultSize = resultSize; |
88 | |
89 | if (!isRunning()) { |
90 | start(LowPriority); |
91 | } else { |
92 | restart = true; |
93 | condition.wakeOne(); |
94 | } |
95 | } |
96 | //! [2] |
97 | |
98 | //! [3] |
99 | void RenderThread::run() |
100 | { |
101 | forever { |
102 | mutex.lock(); |
103 | const double devicePixelRatio = this->devicePixelRatio; |
104 | const QSize resultSize = this->resultSize * devicePixelRatio; |
105 | const double requestedScaleFactor = this->scaleFactor; |
106 | const double scaleFactor = requestedScaleFactor / devicePixelRatio; |
107 | const double centerX = this->centerX; |
108 | const double centerY = this->centerY; |
109 | mutex.unlock(); |
110 | //! [3] |
111 | |
112 | //! [4] |
113 | int halfWidth = resultSize.width() / 2; |
114 | //! [4] //! [5] |
115 | int halfHeight = resultSize.height() / 2; |
116 | QImage image(resultSize, QImage::Format_RGB32); |
117 | image.setDevicePixelRatio(devicePixelRatio); |
118 | |
119 | const int NumPasses = 8; |
120 | int pass = 0; |
121 | while (pass < NumPasses) { |
122 | const int MaxIterations = (1 << (2 * pass + 6)) + 32; |
123 | const int Limit = 4; |
124 | bool allBlack = true; |
125 | |
126 | for (int y = -halfHeight; y < halfHeight; ++y) { |
127 | if (restart) |
128 | break; |
129 | if (abort) |
130 | return; |
131 | |
132 | auto scanLine = |
133 | reinterpret_cast<uint *>(image.scanLine(y + halfHeight)); |
134 | const double ay = centerY + (y * scaleFactor); |
135 | |
136 | for (int x = -halfWidth; x < halfWidth; ++x) { |
137 | const double ax = centerX + (x * scaleFactor); |
138 | double a1 = ax; |
139 | double b1 = ay; |
140 | int numIterations = 0; |
141 | |
142 | do { |
143 | ++numIterations; |
144 | const double a2 = (a1 * a1) - (b1 * b1) + ax; |
145 | const double b2 = (2 * a1 * b1) + ay; |
146 | if ((a2 * a2) + (b2 * b2) > Limit) |
147 | break; |
148 | |
149 | ++numIterations; |
150 | a1 = (a2 * a2) - (b2 * b2) + ax; |
151 | b1 = (2 * a2 * b2) + ay; |
152 | if ((a1 * a1) + (b1 * b1) > Limit) |
153 | break; |
154 | } while (numIterations < MaxIterations); |
155 | |
156 | if (numIterations < MaxIterations) { |
157 | *scanLine++ = colormap[numIterations % ColormapSize]; |
158 | allBlack = false; |
159 | } else { |
160 | *scanLine++ = qRgb(r: 0, g: 0, b: 0); |
161 | } |
162 | } |
163 | } |
164 | |
165 | if (allBlack && pass == 0) { |
166 | pass = 4; |
167 | } else { |
168 | if (!restart) |
169 | emit renderedImage(image, scaleFactor: requestedScaleFactor); |
170 | //! [5] //! [6] |
171 | ++pass; |
172 | } |
173 | //! [6] //! [7] |
174 | } |
175 | //! [7] |
176 | |
177 | //! [8] |
178 | mutex.lock(); |
179 | //! [8] //! [9] |
180 | if (!restart) |
181 | condition.wait(lockedMutex: &mutex); |
182 | restart = false; |
183 | mutex.unlock(); |
184 | } |
185 | } |
186 | //! [9] |
187 | |
188 | //! [10] |
189 | uint RenderThread::rgbFromWaveLength(double wave) |
190 | { |
191 | double r = 0; |
192 | double g = 0; |
193 | double b = 0; |
194 | |
195 | if (wave >= 380.0 && wave <= 440.0) { |
196 | r = -1.0 * (wave - 440.0) / (440.0 - 380.0); |
197 | b = 1.0; |
198 | } else if (wave >= 440.0 && wave <= 490.0) { |
199 | g = (wave - 440.0) / (490.0 - 440.0); |
200 | b = 1.0; |
201 | } else if (wave >= 490.0 && wave <= 510.0) { |
202 | g = 1.0; |
203 | b = -1.0 * (wave - 510.0) / (510.0 - 490.0); |
204 | } else if (wave >= 510.0 && wave <= 580.0) { |
205 | r = (wave - 510.0) / (580.0 - 510.0); |
206 | g = 1.0; |
207 | } else if (wave >= 580.0 && wave <= 645.0) { |
208 | r = 1.0; |
209 | g = -1.0 * (wave - 645.0) / (645.0 - 580.0); |
210 | } else if (wave >= 645.0 && wave <= 780.0) { |
211 | r = 1.0; |
212 | } |
213 | |
214 | double s = 1.0; |
215 | if (wave > 700.0) |
216 | s = 0.3 + 0.7 * (780.0 - wave) / (780.0 - 700.0); |
217 | else if (wave < 420.0) |
218 | s = 0.3 + 0.7 * (wave - 380.0) / (420.0 - 380.0); |
219 | |
220 | r = std::pow(x: r * s, y: 0.8); |
221 | g = std::pow(x: g * s, y: 0.8); |
222 | b = std::pow(x: b * s, y: 0.8); |
223 | return qRgb(r: int(r * 255), g: int(g * 255), b: int(b * 255)); |
224 | } |
225 | //! [10] |
226 | |