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 Qt Data Visualization module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL$
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 General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 or (at your option) any later version
20** approved by the KDE Free Qt Foundation. The licenses are as published by
21** the Free Software Foundation and appearing in the file LICENSE.GPL3
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include "texturehelper_p.h"
31#include "utils_p.h"
32
33#include <QtGui/QImage>
34#include <QtGui/QPainter>
35#include <QtCore/QTime>
36
37QT_BEGIN_NAMESPACE_DATAVISUALIZATION
38
39// Defined in shaderhelper.cpp
40extern void discardDebugMsgs(QtMsgType type, const QMessageLogContext &context, const QString &msg);
41
42TextureHelper::TextureHelper()
43{
44 initializeOpenGLFunctions();
45#if !defined(QT_OPENGL_ES_2)
46 if (!Utils::isOpenGLES()) {
47 // Discard warnings about deprecated functions
48 QtMessageHandler handler = qInstallMessageHandler(discardDebugMsgs);
49
50 m_openGlFunctions_2_1 =
51 QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_2_1>();
52 if (m_openGlFunctions_2_1)
53 m_openGlFunctions_2_1->initializeOpenGLFunctions();
54
55 // Restore original message handler
56 qInstallMessageHandler(handler);
57
58 if (!m_openGlFunctions_2_1)
59 qFatal(msg: "OpenGL version is too low, at least 2.1 is required");
60 }
61#endif
62}
63
64TextureHelper::~TextureHelper()
65{
66}
67
68GLuint TextureHelper::create2DTexture(const QImage &image, bool useTrilinearFiltering,
69 bool convert, bool smoothScale, bool clampY)
70{
71 if (image.isNull())
72 return 0;
73
74 QImage texImage = image;
75
76 if (Utils::isOpenGLES()) {
77 GLuint imageWidth = Utils::getNearestPowerOfTwo(value: image.width());
78 GLuint imageHeight = Utils::getNearestPowerOfTwo(value: image.height());
79 if (smoothScale) {
80 texImage = image.scaled(w: imageWidth, h: imageHeight, aspectMode: Qt::IgnoreAspectRatio,
81 mode: Qt::SmoothTransformation);
82 } else {
83 texImage = image.scaled(w: imageWidth, h: imageHeight, aspectMode: Qt::IgnoreAspectRatio);
84 }
85 }
86
87 GLuint textureId;
88 glGenTextures(n: 1, textures: &textureId);
89 glBindTexture(GL_TEXTURE_2D, texture: textureId);
90 if (convert)
91 texImage = convertToGLFormat(srcImage: texImage);
92 glTexImage2D(GL_TEXTURE_2D, level: 0, GL_RGBA, width: texImage.width(), height: texImage.height(),
93 border: 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels: texImage.bits());
94 if (smoothScale)
95 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
96 else
97 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
98 if (useTrilinearFiltering) {
99 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
100 glGenerateMipmap(GL_TEXTURE_2D);
101 } else {
102 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
103 }
104 if (clampY)
105 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
106 glBindTexture(GL_TEXTURE_2D, texture: 0);
107
108 return textureId;
109}
110
111GLuint TextureHelper::create3DTexture(const QVector<uchar> *data, int width, int height, int depth,
112 QImage::Format dataFormat)
113{
114 if (Utils::isOpenGLES() || !width || !height || !depth)
115 return 0;
116
117 GLuint textureId = 0;
118#if defined(QT_OPENGL_ES_2)
119 Q_UNUSED(dataFormat)
120 Q_UNUSED(data)
121#else
122 glEnable(GL_TEXTURE_3D);
123
124 glGenTextures(n: 1, textures: &textureId);
125 glBindTexture(GL_TEXTURE_3D, texture: textureId);
126 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
127 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
128 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
129 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
130 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
131
132 GLenum status = glGetError();
133 // glGetError docs advise to call glGetError in loop to clear all error flags
134 while (status)
135 status = glGetError();
136
137 GLint internalFormat = 4;
138 GLint format = GL_BGRA;
139 if (dataFormat == QImage::Format_Indexed8) {
140 internalFormat = 1;
141 format = GL_RED;
142 // Align width to 32bits
143 width = width + width % 4;
144 }
145 m_openGlFunctions_2_1->glTexImage3D(GL_TEXTURE_3D, level: 0, internalformat: internalFormat, width, height, depth, border: 0,
146 format, GL_UNSIGNED_BYTE, pixels: data->constData());
147 status = glGetError();
148 if (status)
149 qWarning() << __FUNCTION__ << "3D texture creation failed:" << status;
150
151 glBindTexture(GL_TEXTURE_3D, texture: 0);
152 glDisable(GL_TEXTURE_3D);
153#endif
154 return textureId;
155}
156
157GLuint TextureHelper::createCubeMapTexture(const QImage &image, bool useTrilinearFiltering)
158{
159 if (image.isNull())
160 return 0;
161
162 GLuint textureId;
163 glGenTextures(n: 1, textures: &textureId);
164 glBindTexture(GL_TEXTURE_CUBE_MAP, texture: textureId);
165 QImage glTexture = convertToGLFormat(srcImage: image);
166 glTexImage2D(GL_TEXTURE_CUBE_MAP, level: 0, GL_RGBA, width: glTexture.width(), height: glTexture.height(),
167 border: 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels: glTexture.bits());
168 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
169 if (useTrilinearFiltering) {
170 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
171 glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
172 } else {
173 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
174 }
175 glBindTexture(GL_TEXTURE_2D, texture: 0);
176 return textureId;
177}
178
179GLuint TextureHelper::createSelectionTexture(const QSize &size, GLuint &frameBuffer,
180 GLuint &depthBuffer)
181{
182 GLuint textureid;
183
184 // Create texture for the selection buffer
185 glGenTextures(n: 1, textures: &textureid);
186 glBindTexture(GL_TEXTURE_2D, texture: textureid);
187 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
188 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
189 glTexImage2D(GL_TEXTURE_2D, level: 0, GL_RGBA, width: size.width(), height: size.height(), border: 0, GL_RGBA,
190 GL_UNSIGNED_BYTE, NULL);
191 glBindTexture(GL_TEXTURE_2D, texture: 0);
192
193 // Create render buffer
194 if (depthBuffer)
195 glDeleteRenderbuffers(n: 1, renderbuffers: &depthBuffer);
196
197 glGenRenderbuffers(n: 1, renderbuffers: &depthBuffer);
198 glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer: depthBuffer);
199 GLenum status = glGetError();
200 // glGetError docs advise to call glGetError in loop to clear all error flags
201 while (status)
202 status = glGetError();
203 if (Utils::isOpenGLES())
204 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width: size.width(), height: size.height());
205 else
206 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width: size.width(), height: size.height());
207
208 status = glGetError();
209 if (status) {
210 qCritical() << "Selection texture render buffer creation failed:" << status;
211 glDeleteTextures(n: 1, textures: &textureid);
212 glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer: 0);
213 return 0;
214 }
215 glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer: 0);
216
217 // Create frame buffer
218 if (!frameBuffer)
219 glGenFramebuffers(n: 1, framebuffers: &frameBuffer);
220 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: frameBuffer);
221
222 // Attach texture to color attachment
223 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture: textureid, level: 0);
224 // Attach renderbuffer to depth attachment
225 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbuffer: depthBuffer);
226
227 // Verify that the frame buffer is complete
228 status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
229 if (status != GL_FRAMEBUFFER_COMPLETE) {
230 qCritical() << "Selection texture frame buffer creation failed:" << status;
231 glDeleteTextures(n: 1, textures: &textureid);
232 textureid = 0;
233 }
234
235 // Restore the default framebuffer
236 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: 0);
237
238 return textureid;
239}
240
241GLuint TextureHelper::createCursorPositionTexture(const QSize &size, GLuint &frameBuffer)
242{
243 GLuint textureid;
244 glGenTextures(n: 1, textures: &textureid);
245 glBindTexture(GL_TEXTURE_2D, texture: textureid);
246 glTexImage2D(GL_TEXTURE_2D, level: 0, GL_RGBA, width: size.width(), height: size.height(), border: 0, GL_RGBA,
247 GL_UNSIGNED_BYTE, NULL);
248 glBindTexture(GL_TEXTURE_2D, texture: 0);
249
250 glGenFramebuffers(n: 1, framebuffers: &frameBuffer);
251 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: frameBuffer);
252 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
253 texture: textureid, level: 0);
254
255 GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
256 if (status != GL_FRAMEBUFFER_COMPLETE) {
257 qCritical() << "Cursor position mapper frame buffer creation failed:" << status;
258 glDeleteTextures(n: 1, textures: &textureid);
259 textureid = 0;
260 }
261 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: 0);
262
263 return textureid;
264}
265
266GLuint TextureHelper::createUniformTexture(const QColor &color)
267{
268 QImage image(QSize(int(uniformTextureWidth), int(uniformTextureHeight)),
269 QImage::Format_RGB32);
270 QPainter pmp(&image);
271 pmp.setBrush(QBrush(color));
272 pmp.setPen(Qt::NoPen);
273 pmp.drawRect(x: 0, y: 0, w: int(uniformTextureWidth), h: int(uniformTextureHeight));
274
275 return create2DTexture(image, useTrilinearFiltering: false, convert: true, smoothScale: false, clampY: true);
276}
277
278GLuint TextureHelper::createGradientTexture(const QLinearGradient &gradient)
279{
280 QImage image(QSize(int(gradientTextureWidth), int(gradientTextureHeight)),
281 QImage::Format_RGB32);
282 QPainter pmp(&image);
283 pmp.setBrush(QBrush(gradient));
284 pmp.setPen(Qt::NoPen);
285 pmp.drawRect(x: 0, y: 0, w: int(gradientTextureWidth), h: int(gradientTextureHeight));
286
287 return create2DTexture(image, useTrilinearFiltering: false, convert: true, smoothScale: false, clampY: true);
288}
289
290GLuint TextureHelper::createDepthTexture(const QSize &size, GLuint textureSize)
291{
292 GLuint depthtextureid = 0;
293#if defined(QT_OPENGL_ES_2)
294 Q_UNUSED(size)
295 Q_UNUSED(textureSize)
296#else
297 if (!Utils::isOpenGLES()) {
298 // Create depth texture for the shadow mapping
299 glGenTextures(n: 1, textures: &depthtextureid);
300 glBindTexture(GL_TEXTURE_2D, texture: depthtextureid);
301 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
302 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
303 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
304 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
305 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
306 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
307 glTexImage2D(GL_TEXTURE_2D, level: 0, GL_DEPTH_COMPONENT, width: size.width() * textureSize,
308 height: size.height() * textureSize, border: 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
309 glBindTexture(GL_TEXTURE_2D, texture: 0);
310 }
311#endif
312 return depthtextureid;
313}
314
315GLuint TextureHelper::createDepthTextureFrameBuffer(const QSize &size, GLuint &frameBuffer,
316 GLuint textureSize)
317{
318 GLuint depthtextureid = createDepthTexture(size, textureSize);
319#if defined(QT_OPENGL_ES_2)
320 Q_UNUSED(frameBuffer)
321#else
322 if (!Utils::isOpenGLES()) {
323 // Create frame buffer
324 if (!frameBuffer)
325 glGenFramebuffers(n: 1, framebuffers: &frameBuffer);
326 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: frameBuffer);
327
328 // Attach texture to depth attachment
329 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texture: depthtextureid, level: 0);
330
331 m_openGlFunctions_2_1->glDrawBuffer(GL_NONE);
332 m_openGlFunctions_2_1->glReadBuffer(GL_NONE);
333
334 // Verify that the frame buffer is complete
335 GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
336 if (status != GL_FRAMEBUFFER_COMPLETE) {
337 qCritical() << "Depth texture frame buffer creation failed" << status;
338 glDeleteTextures(n: 1, textures: &depthtextureid);
339 depthtextureid = 0;
340 }
341
342 // Restore the default framebuffer
343 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: 0);
344 }
345#endif
346 return depthtextureid;
347}
348
349void TextureHelper::deleteTexture(GLuint *texture)
350{
351 if (texture && *texture) {
352 if (QOpenGLContext::currentContext())
353 glDeleteTextures(n: 1, textures: texture);
354 *texture = 0;
355 }
356}
357
358QImage TextureHelper::convertToGLFormat(const QImage &srcImage)
359{
360 QImage res(srcImage.size(), QImage::Format_ARGB32);
361 convertToGLFormatHelper(dstImage&: res, srcImage: srcImage.convertToFormat(f: QImage::Format_ARGB32), GL_RGBA);
362 return res;
363}
364
365void TextureHelper::convertToGLFormatHelper(QImage &dstImage, const QImage &srcImage,
366 GLenum texture_format)
367{
368 Q_ASSERT(dstImage.depth() == 32);
369 Q_ASSERT(srcImage.depth() == 32);
370
371 if (dstImage.size() != srcImage.size()) {
372 int target_width = dstImage.width();
373 int target_height = dstImage.height();
374 float sx = target_width / float(srcImage.width());
375 float sy = target_height / float(srcImage.height());
376
377 quint32 *dest = (quint32 *) dstImage.scanLine(0); // NB! avoid detach here
378 uchar *srcPixels = (uchar *) srcImage.scanLine(srcImage.height() - 1);
379 int sbpl = srcImage.bytesPerLine();
380 int dbpl = dstImage.bytesPerLine();
381
382 int ix = int(0x00010000 / sx);
383 int iy = int(0x00010000 / sy);
384
385 quint32 basex = int(0.5 * ix);
386 quint32 srcy = int(0.5 * iy);
387
388 // scale, swizzle and mirror in one loop
389 while (target_height--) {
390 const uint *src = (const quint32 *) (srcPixels - (srcy >> 16) * sbpl);
391 int srcx = basex;
392 for (int x=0; x<target_width; ++x) {
393 dest[x] = qt_gl_convertToGLFormatHelper(src_pixel: src[srcx >> 16], texture_format);
394 srcx += ix;
395 }
396 dest = (quint32 *)(((uchar *) dest) + dbpl);
397 srcy += iy;
398 }
399 } else {
400 const int width = srcImage.width();
401 const int height = srcImage.height();
402 const uint *p = (const uint*) srcImage.scanLine(srcImage.height() - 1);
403 uint *q = (uint*) dstImage.scanLine(0);
404
405#if !defined(QT_OPENGL_ES_2)
406 if (texture_format == GL_BGRA) {
407#else
408 if (texture_format == GL_BGRA8_EXT) {
409#endif
410 if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
411 // mirror + swizzle
412 for (int i=0; i < height; ++i) {
413 const uint *end = p + width;
414 while (p < end) {
415 *q = ((*p << 24) & 0xff000000)
416 | ((*p >> 24) & 0x000000ff)
417 | ((*p << 8) & 0x00ff0000)
418 | ((*p >> 8) & 0x0000ff00);
419 p++;
420 q++;
421 }
422 p -= 2 * width;
423 }
424 } else {
425 const uint bytesPerLine = srcImage.bytesPerLine();
426 for (int i=0; i < height; ++i) {
427 memcpy(dest: q, src: p, n: bytesPerLine);
428 q += width;
429 p -= width;
430 }
431 }
432 } else {
433 if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
434 for (int i=0; i < height; ++i) {
435 const uint *end = p + width;
436 while (p < end) {
437 *q = (*p << 8) | ((*p >> 24) & 0xff);
438 p++;
439 q++;
440 }
441 p -= 2 * width;
442 }
443 } else {
444 for (int i=0; i < height; ++i) {
445 const uint *end = p + width;
446 while (p < end) {
447 *q = ((*p << 16) & 0xff0000) | ((*p >> 16) & 0xff) | (*p & 0xff00ff00);
448 p++;
449 q++;
450 }
451 p -= 2 * width;
452 }
453 }
454 }
455 }
456}
457
458QRgb TextureHelper::qt_gl_convertToGLFormatHelper(QRgb src_pixel, GLenum texture_format)
459{
460#if !defined(QT_OPENGL_ES_2)
461 if (texture_format == GL_BGRA) {
462#else
463 if (texture_format == GL_BGRA8_EXT) {
464#endif
465 if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
466 return ((src_pixel << 24) & 0xff000000)
467 | ((src_pixel >> 24) & 0x000000ff)
468 | ((src_pixel << 8) & 0x00ff0000)
469 | ((src_pixel >> 8) & 0x0000ff00);
470 } else {
471 return src_pixel;
472 }
473 } else { // GL_RGBA
474 if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
475 return (src_pixel << 8) | ((src_pixel >> 24) & 0xff);
476 } else {
477 return ((src_pixel << 16) & 0xff0000)
478 | ((src_pixel >> 16) & 0xff)
479 | (src_pixel & 0xff00ff00);
480 }
481 }
482}
483
484QT_END_NAMESPACE_DATAVISUALIZATION
485

source code of qtdatavis3d/src/datavisualization/utils/texturehelper.cpp