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 QtGui 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 QtGui
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 */
QOpenGLPaintDevice()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 */
QOpenGLPaintDevice(const QSize & size)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 */
QOpenGLPaintDevice(int width,int height)139 QOpenGLPaintDevice::QOpenGLPaintDevice(int width, int height)
140     : QOpenGLPaintDevice(QSize(width, height))
141 {
142 }
143 
144 /*!
145     \internal
146  */
QOpenGLPaintDevice(QOpenGLPaintDevicePrivate & dd)147 QOpenGLPaintDevice::QOpenGLPaintDevice(QOpenGLPaintDevicePrivate &dd)
148     : d_ptr(&dd)
149 {
150 }
151 
152 /*!
153     Destroys the QOpenGLPaintDevice.
154 */
155 
~QOpenGLPaintDevice()156 QOpenGLPaintDevice::~QOpenGLPaintDevice()
157 {
158     delete d_ptr->engine;
159 }
160 
161 /*!
162     \fn int QOpenGLPaintDevice::devType() const
163     \internal
164     \reimp
165 */
166 
QOpenGLPaintDevicePrivate(const QSize & sz)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 
~QOpenGLPaintDevicePrivate()178 QOpenGLPaintDevicePrivate::~QOpenGLPaintDevicePrivate()
179 {
180 }
181 
182 class QOpenGLEngineThreadStorage
183 {
184 public:
engine()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 
Q_GLOBAL_STATIC(QOpenGLEngineThreadStorage,qt_opengl_engine)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 
context() const220 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 
size() const231 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 
setSize(const QSize & size)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 */
setDevicePixelRatio(qreal devicePixelRatio)250 void QOpenGLPaintDevice::setDevicePixelRatio(qreal devicePixelRatio)
251 {
252     d_ptr->devicePixelRatio = devicePixelRatio;
253 }
254 
255 /*!
256     \reimp
257 */
258 
metric(QPaintDevice::PaintDeviceMetric metric) const259 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 
dotsPerMeterX() const299 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 
dotsPerMeterY() const310 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 
setDotsPerMeterX(qreal dpmx)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 
setDotsPerMeterY(qreal dpmy)332 void QOpenGLPaintDevice::setDotsPerMeterY(qreal dpmy)
333 {
334     d_ptr->dpmy = 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 */
setPaintFlipped(bool flipped)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 
paintFlipped() const353 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 */
ensureActiveTarget()368 void QOpenGLPaintDevice::ensureActiveTarget()
369 {
370 }
371 
372 QT_END_NAMESPACE
373