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

source code of qtbase/src/opengl/qopenglpaintdevice.cpp