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 | |
19 | QT_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 | */ |
79 | QOpenGLPaintDevice::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 | */ |
91 | QOpenGLPaintDevice::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 | */ |
103 | QOpenGLPaintDevice::QOpenGLPaintDevice(int width, int height) |
104 | : QOpenGLPaintDevice(QSize(width, height)) |
105 | { |
106 | } |
107 | |
108 | /*! |
109 | \internal |
110 | */ |
111 | QOpenGLPaintDevice::QOpenGLPaintDevice(QOpenGLPaintDevicePrivate &dd) |
112 | : d_ptr(&dd) |
113 | { |
114 | } |
115 | |
116 | /*! |
117 | Destroys the QOpenGLPaintDevice. |
118 | */ |
119 | |
120 | QOpenGLPaintDevice::~QOpenGLPaintDevice() |
121 | { |
122 | delete d_ptr->engine; |
123 | } |
124 | |
125 | /*! |
126 | \fn int QOpenGLPaintDevice::devType() const |
127 | \internal |
128 | \reimp |
129 | */ |
130 | |
131 | QOpenGLPaintDevicePrivate::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 | |
142 | QOpenGLPaintDevicePrivate::~QOpenGLPaintDevicePrivate() |
143 | { |
144 | } |
145 | |
146 | class QOpenGLEngineThreadStorage |
147 | { |
148 | public: |
149 | QPaintEngine *engine() { |
150 | QPaintEngine *&localEngine = storage.localData(); |
151 | if (!localEngine) |
152 | localEngine = new QOpenGL2PaintEngineEx; |
153 | return localEngine; |
154 | } |
155 | |
156 | private: |
157 | QThreadStorage<QPaintEngine *> storage; |
158 | }; |
159 | |
160 | Q_GLOBAL_STATIC(QOpenGLEngineThreadStorage, qt_opengl_engine) |
161 | |
162 | /*! |
163 | \reimp |
164 | */ |
165 | |
166 | QPaintEngine *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 | |
184 | QOpenGLContext *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 | |
195 | QSize 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 | |
206 | void 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 | */ |
214 | void QOpenGLPaintDevice::setDevicePixelRatio(qreal devicePixelRatio) |
215 | { |
216 | d_ptr->devicePixelRatio = devicePixelRatio; |
217 | } |
218 | |
219 | /*! |
220 | \reimp |
221 | */ |
222 | |
223 | int 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 | |
263 | qreal 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 | |
274 | qreal 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 | |
285 | void 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 | |
296 | void 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 | */ |
306 | void 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 | |
317 | bool 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 | */ |
332 | void QOpenGLPaintDevice::ensureActiveTarget() |
333 | { |
334 | } |
335 | |
336 | QT_END_NAMESPACE |
337 | |