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 QtOpenGL module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
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 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include <qopenglpaintdevice.h> |
41 | #include <qpaintengine.h> |
42 | #include <qthreadstorage.h> |
43 | |
44 | #include <private/qopenglpaintdevice_p.h> |
45 | #include <private/qobject_p.h> |
46 | #include <private/qopenglcontext_p.h> |
47 | #include <private/qopenglframebufferobject_p.h> |
48 | #include <private/qopenglpaintengine_p.h> |
49 | |
50 | // for qt_defaultDpiX/Y |
51 | #include <private/qfont_p.h> |
52 | |
53 | #include <qopenglfunctions.h> |
54 | |
55 | QT_BEGIN_NAMESPACE |
56 | |
57 | /*! |
58 | \class QOpenGLPaintDevice |
59 | \brief The QOpenGLPaintDevice class enables painting to an OpenGL context using QPainter. |
60 | \since 5.0 |
61 | \inmodule QtOpenGL |
62 | |
63 | \ingroup painting-3D |
64 | |
65 | The QOpenGLPaintDevice uses the \b current QOpenGL context to render |
66 | QPainter draw commands. The context is captured upon construction. It |
67 | requires support for OpenGL (ES) 2.0 or higher. |
68 | |
69 | \section1 Performance |
70 | |
71 | The QOpenGLPaintDevice is almost always hardware accelerated and |
72 | has the potential of being much faster than software |
73 | rasterization. However, it is more sensitive to state changes, and |
74 | therefore requires the drawing commands to be carefully ordered to |
75 | achieve optimal performance. |
76 | |
77 | \section1 Antialiasing and Quality |
78 | |
79 | Antialiasing in the OpenGL paint engine is done using |
80 | multisampling. Most hardware require significantly more memory to |
81 | do multisampling and the resulting quality is not on par with the |
82 | quality of the software paint engine. The OpenGL paint engine's |
83 | strength lies in its performance, not its visual rendering |
84 | quality. |
85 | |
86 | \section1 State Changes |
87 | |
88 | When painting to a QOpenGLPaintDevice using QPainter, the state of |
89 | the current OpenGL context will be altered by the paint engine to |
90 | reflect its needs. Applications should not rely upon the OpenGL |
91 | state being reset to its original conditions, particularly the |
92 | current shader program, OpenGL viewport, texture units, and |
93 | drawing modes. |
94 | |
95 | \section1 Mixing QPainter and OpenGL |
96 | |
97 | When intermixing QPainter and OpenGL, it is important to notify |
98 | QPainter that the OpenGL state may have been cluttered so it can |
99 | restore its internal state. This is achieved by calling \l |
100 | QPainter::beginNativePainting() before starting the OpenGL |
101 | rendering and calling \l QPainter::endNativePainting() after |
102 | finishing. |
103 | |
104 | \sa {OpenGL Window Example} |
105 | |
106 | */ |
107 | |
108 | /*! |
109 | Constructs a QOpenGLPaintDevice. |
110 | |
111 | The QOpenGLPaintDevice is only valid for the current context. |
112 | |
113 | \sa QOpenGLContext::currentContext() |
114 | */ |
115 | QOpenGLPaintDevice::QOpenGLPaintDevice() |
116 | : d_ptr(new QOpenGLPaintDevicePrivate(QSize())) |
117 | { |
118 | } |
119 | |
120 | /*! |
121 | Constructs a QOpenGLPaintDevice with the given \a size. |
122 | |
123 | The QOpenGLPaintDevice is only valid for the current context. |
124 | |
125 | \sa QOpenGLContext::currentContext() |
126 | */ |
127 | QOpenGLPaintDevice::QOpenGLPaintDevice(const QSize &size) |
128 | : d_ptr(new QOpenGLPaintDevicePrivate(size)) |
129 | { |
130 | } |
131 | |
132 | /*! |
133 | Constructs a QOpenGLPaintDevice with the given \a width and \a height. |
134 | |
135 | The QOpenGLPaintDevice is only valid for the current context. |
136 | |
137 | \sa QOpenGLContext::currentContext() |
138 | */ |
139 | QOpenGLPaintDevice::QOpenGLPaintDevice(int width, int height) |
140 | : QOpenGLPaintDevice(QSize(width, height)) |
141 | { |
142 | } |
143 | |
144 | /*! |
145 | \internal |
146 | */ |
147 | QOpenGLPaintDevice::QOpenGLPaintDevice(QOpenGLPaintDevicePrivate &dd) |
148 | : d_ptr(&dd) |
149 | { |
150 | } |
151 | |
152 | /*! |
153 | Destroys the QOpenGLPaintDevice. |
154 | */ |
155 | |
156 | QOpenGLPaintDevice::~QOpenGLPaintDevice() |
157 | { |
158 | delete d_ptr->engine; |
159 | } |
160 | |
161 | /*! |
162 | \fn int QOpenGLPaintDevice::devType() const |
163 | \internal |
164 | \reimp |
165 | */ |
166 | |
167 | QOpenGLPaintDevicePrivate::QOpenGLPaintDevicePrivate(const QSize &sz) |
168 | : size(sz) |
169 | , ctx(QOpenGLContext::currentContext()) |
170 | , dpmx(qt_defaultDpiX() * 100. / 2.54) |
171 | , dpmy(qt_defaultDpiY() * 100. / 2.54) |
172 | , devicePixelRatio(1.0) |
173 | , flipped(false) |
174 | , engine(nullptr) |
175 | { |
176 | } |
177 | |
178 | QOpenGLPaintDevicePrivate::~QOpenGLPaintDevicePrivate() |
179 | { |
180 | } |
181 | |
182 | class QOpenGLEngineThreadStorage |
183 | { |
184 | public: |
185 | QPaintEngine *engine() { |
186 | QPaintEngine *&localEngine = storage.localData(); |
187 | if (!localEngine) |
188 | localEngine = new QOpenGL2PaintEngineEx; |
189 | return localEngine; |
190 | } |
191 | |
192 | private: |
193 | QThreadStorage<QPaintEngine *> storage; |
194 | }; |
195 | |
196 | Q_GLOBAL_STATIC(QOpenGLEngineThreadStorage, qt_opengl_engine) |
197 | |
198 | /*! |
199 | \reimp |
200 | */ |
201 | |
202 | QPaintEngine *QOpenGLPaintDevice::paintEngine() const |
203 | { |
204 | if (d_ptr->engine) |
205 | return d_ptr->engine; |
206 | |
207 | QPaintEngine *engine = qt_opengl_engine()->engine(); |
208 | if (engine->isActive() && engine->paintDevice() != this) { |
209 | d_ptr->engine = new QOpenGL2PaintEngineEx; |
210 | return d_ptr->engine; |
211 | } |
212 | |
213 | return engine; |
214 | } |
215 | |
216 | /*! |
217 | Returns the OpenGL context associated with the paint device. |
218 | */ |
219 | |
220 | QOpenGLContext *QOpenGLPaintDevice::context() const |
221 | { |
222 | return d_ptr->ctx; |
223 | } |
224 | |
225 | /*! |
226 | Returns the pixel size of the paint device. |
227 | |
228 | \sa setSize() |
229 | */ |
230 | |
231 | QSize QOpenGLPaintDevice::size() const |
232 | { |
233 | return d_ptr->size; |
234 | } |
235 | |
236 | /*! |
237 | Sets the pixel size of the paint device to \a size. |
238 | |
239 | \sa size() |
240 | */ |
241 | |
242 | void QOpenGLPaintDevice::setSize(const QSize &size) |
243 | { |
244 | d_ptr->size = size; |
245 | } |
246 | |
247 | /*! |
248 | Sets the device pixel ratio for the paint device to \a devicePixelRatio. |
249 | */ |
250 | void QOpenGLPaintDevice::setDevicePixelRatio(qreal devicePixelRatio) |
251 | { |
252 | d_ptr->devicePixelRatio = devicePixelRatio; |
253 | } |
254 | |
255 | /*! |
256 | \reimp |
257 | */ |
258 | |
259 | int QOpenGLPaintDevice::metric(QPaintDevice::PaintDeviceMetric metric) const |
260 | { |
261 | switch (metric) { |
262 | case PdmWidth: |
263 | return d_ptr->size.width(); |
264 | case PdmHeight: |
265 | return d_ptr->size.height(); |
266 | case PdmDepth: |
267 | return 32; |
268 | case PdmWidthMM: |
269 | return qRound(d_ptr->size.width() * 1000 / d_ptr->dpmx); |
270 | case PdmHeightMM: |
271 | return qRound(d_ptr->size.height() * 1000 / d_ptr->dpmy); |
272 | case PdmNumColors: |
273 | return 0; |
274 | case PdmDpiX: |
275 | return qRound(d_ptr->dpmx * 0.0254); |
276 | case PdmDpiY: |
277 | return qRound(d_ptr->dpmy * 0.0254); |
278 | case PdmPhysicalDpiX: |
279 | return qRound(d_ptr->dpmx * 0.0254); |
280 | case PdmPhysicalDpiY: |
281 | return qRound(d_ptr->dpmy * 0.0254); |
282 | case PdmDevicePixelRatio: |
283 | return d_ptr->devicePixelRatio; |
284 | case PdmDevicePixelRatioScaled: |
285 | return d_ptr->devicePixelRatio * QPaintDevice::devicePixelRatioFScale(); |
286 | |
287 | default: |
288 | qWarning("QOpenGLPaintDevice::metric() - metric %d not known" , metric); |
289 | return 0; |
290 | } |
291 | } |
292 | |
293 | /*! |
294 | Returns the number of pixels per meter horizontally. |
295 | |
296 | \sa setDotsPerMeterX() |
297 | */ |
298 | |
299 | qreal QOpenGLPaintDevice::dotsPerMeterX() const |
300 | { |
301 | return d_ptr->dpmx; |
302 | } |
303 | |
304 | /*! |
305 | Returns the number of pixels per meter vertically. |
306 | |
307 | \sa setDotsPerMeterY() |
308 | */ |
309 | |
310 | qreal QOpenGLPaintDevice::dotsPerMeterY() const |
311 | { |
312 | return d_ptr->dpmy; |
313 | } |
314 | |
315 | /*! |
316 | Sets the number of pixels per meter horizontally to \a dpmx. |
317 | |
318 | \sa dotsPerMeterX() |
319 | */ |
320 | |
321 | void QOpenGLPaintDevice::setDotsPerMeterX(qreal dpmx) |
322 | { |
323 | d_ptr->dpmx = dpmx; |
324 | } |
325 | |
326 | /*! |
327 | Sets the number of pixels per meter vertically to \a dpmy. |
328 | |
329 | \sa dotsPerMeterY() |
330 | */ |
331 | |
332 | void QOpenGLPaintDevice::setDotsPerMeterY(qreal dpmy) |
333 | { |
334 | d_ptr->dpmx = dpmy; |
335 | } |
336 | |
337 | /*! |
338 | Sets whether painting should be flipped around the Y-axis or not to \a flipped. |
339 | |
340 | \sa paintFlipped() |
341 | */ |
342 | void QOpenGLPaintDevice::setPaintFlipped(bool flipped) |
343 | { |
344 | d_ptr->flipped = flipped; |
345 | } |
346 | |
347 | /*! |
348 | Returns \c true if painting is flipped around the Y-axis. |
349 | |
350 | \sa setPaintFlipped() |
351 | */ |
352 | |
353 | bool QOpenGLPaintDevice::paintFlipped() const |
354 | { |
355 | return d_ptr->flipped; |
356 | } |
357 | |
358 | /*! |
359 | This virtual method is provided as a callback to allow re-binding a target |
360 | frame buffer object or context when different QOpenGLPaintDevice instances |
361 | are issuing draw calls alternately. |
362 | |
363 | \l{QPainter::beginNativePainting()}{beginNativePainting()} will also trigger |
364 | this method. |
365 | |
366 | The default implementation does nothing. |
367 | */ |
368 | void QOpenGLPaintDevice::ensureActiveTarget() |
369 | { |
370 | } |
371 | |
372 | QT_END_NAMESPACE |
373 | |