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

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