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
55QT_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*/
115QOpenGLPaintDevice::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*/
127QOpenGLPaintDevice::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*/
139QOpenGLPaintDevice::QOpenGLPaintDevice(int width, int height)
140 : QOpenGLPaintDevice(QSize(width, height))
141{
142}
143
144/*!
145 \internal
146 */
147QOpenGLPaintDevice::QOpenGLPaintDevice(QOpenGLPaintDevicePrivate &dd)
148 : d_ptr(&dd)
149{
150}
151
152/*!
153 Destroys the QOpenGLPaintDevice.
154*/
155
156QOpenGLPaintDevice::~QOpenGLPaintDevice()
157{
158 delete d_ptr->engine;
159}
160
161/*!
162 \fn int QOpenGLPaintDevice::devType() const
163 \internal
164 \reimp
165*/
166
167QOpenGLPaintDevicePrivate::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
178QOpenGLPaintDevicePrivate::~QOpenGLPaintDevicePrivate()
179{
180}
181
182class QOpenGLEngineThreadStorage
183{
184public:
185 QPaintEngine *engine() {
186 QPaintEngine *&localEngine = storage.localData();
187 if (!localEngine)
188 localEngine = new QOpenGL2PaintEngineEx;
189 return localEngine;
190 }
191
192private:
193 QThreadStorage<QPaintEngine *> storage;
194};
195
196Q_GLOBAL_STATIC(QOpenGLEngineThreadStorage, qt_opengl_engine)
197
198/*!
199 \reimp
200*/
201
202QPaintEngine *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
220QOpenGLContext *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
231QSize 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
242void 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*/
250void QOpenGLPaintDevice::setDevicePixelRatio(qreal devicePixelRatio)
251{
252 d_ptr->devicePixelRatio = devicePixelRatio;
253}
254
255/*!
256 \reimp
257*/
258
259int 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
299qreal 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
310qreal 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
321void 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
332void 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*/
342void 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
353bool 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*/
368void QOpenGLPaintDevice::ensureActiveTarget()
369{
370}
371
372QT_END_NAMESPACE
373