Revision control

++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++
++ #include "texturehelper_p.h"
++ #include "utils_p.h"
++
++ #include <QtGui/QImage>
++ #include <QtGui/QPainter>
++ #include <QtCore/QTime>
++
++ QT_BEGIN_NAMESPACE
++
++ // Defined in shaderhelper.cpp
++ extern void discardDebugMsgs(QtMsgType type, const QMessageLogContext &context, const QString &msg);
++
++ TextureHelper::TextureHelper()
++ {
++ initializeOpenGLFunctions();
++ #if !QT_CONFIG(opengles2)
++ if (!Utils::isOpenGLES()) {
++ // Discard warnings about deprecated functions
++ QtMessageHandler handler = qInstallMessageHandler(discardDebugMsgs);
++
++ m_openGlFunctions_2_1 = new QOpenGLFunctions_2_1;
++ if (m_openGlFunctions_2_1)
++ m_openGlFunctions_2_1->initializeOpenGLFunctions();
++
++ // Restore original message handler
++ qInstallMessageHandler(handler);
++
++ if (!m_openGlFunctions_2_1)
++ qFatal("OpenGL version is too low, at least 2.1 is required");
++ }
++ #endif
++ }
++
++ TextureHelper::~TextureHelper()
++ {
++ #if !QT_CONFIG(opengles2)
++ delete m_openGlFunctions_2_1;
++ #endif
++ }
++
++ GLuint TextureHelper::create2DTexture(const QImage &image, bool useTrilinearFiltering,
++ bool convert, bool smoothScale, bool clampY)
++ {
++ if (image.isNull())
++ return 0;
++
++ QImage texImage = image;
++
++ if (Utils::isOpenGLES()) {
++ GLuint imageWidth = Utils::getNearestPowerOfTwo(image.width());
++ GLuint imageHeight = Utils::getNearestPowerOfTwo(image.height());
++ if (smoothScale) {
++ texImage = image.scaled(imageWidth, imageHeight, Qt::IgnoreAspectRatio,
++ Qt::SmoothTransformation);
++ } else {
++ texImage = image.scaled(imageWidth, imageHeight, Qt::IgnoreAspectRatio);
++ }
++ }
++
++ GLuint textureId;
++ glGenTextures(1, &textureId);
++ glBindTexture(GL_TEXTURE_2D, textureId);
++ if (convert)
++ texImage = convertToGLFormat(texImage);
++ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texImage.width(), texImage.height(),
++ 0, GL_RGBA, GL_UNSIGNED_BYTE, texImage.bits());
++ if (smoothScale)
++ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
++ else
++ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
++ if (useTrilinearFiltering) {
++ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
++ glGenerateMipmap(GL_TEXTURE_2D);
++ } else {
++ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
++ }
++ if (clampY)
++ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
++ glBindTexture(GL_TEXTURE_2D, 0);
++
++ return textureId;
++ }
++
++ GLuint TextureHelper::create3DTexture(const QList<uchar> *data, int width, int height, int depth,
++ QImage::Format dataFormat)
++ {
++ if (Utils::isOpenGLES() || !width || !height || !depth)
++ return 0;
++
++ GLuint textureId = 0;
++ #if QT_CONFIG(opengles2)
++ Q_UNUSED(dataFormat);
++ Q_UNUSED(data);
++ #else
++ glEnable(GL_TEXTURE_3D);
++
++ glGenTextures(1, &textureId);
++ glBindTexture(GL_TEXTURE_3D, textureId);
++ glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
++ glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
++ glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
++ glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
++ glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
++
++ GLenum status = glGetError();
++ // glGetError docs advise to call glGetError in loop to clear all error flags
++ while (status)
++ status = glGetError();
++
++ GLint internalFormat = 4;
++ GLint format = GL_BGRA;
++ if (dataFormat == QImage::Format_Indexed8) {
++ internalFormat = 1;
++ format = GL_RED;
++ // Align width to 32bits
++ width = width + width % 4;
++ }
++ m_openGlFunctions_2_1->glTexImage3D(GL_TEXTURE_3D, 0, internalFormat, width, height, depth, 0,
++ format, GL_UNSIGNED_BYTE, data->constData());
++ status = glGetError();
++ if (status)
++ qWarning() << __FUNCTION__ << "3D texture creation failed:" << status;
++
++ glBindTexture(GL_TEXTURE_3D, 0);
++ glDisable(GL_TEXTURE_3D);
++ #endif
++ return textureId;
++ }
++
++ GLuint TextureHelper::createCubeMapTexture(const QImage &image, bool useTrilinearFiltering)
++ {
++ if (image.isNull())
++ return 0;
++
++ GLuint textureId;
++ glGenTextures(1, &textureId);
++ glBindTexture(GL_TEXTURE_CUBE_MAP, textureId);
++ QImage glTexture = convertToGLFormat(image);
++ glTexImage2D(GL_TEXTURE_CUBE_MAP, 0, GL_RGBA, glTexture.width(), glTexture.height(),
++ 0, GL_RGBA, GL_UNSIGNED_BYTE, glTexture.bits());
++ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
++ if (useTrilinearFiltering) {
++ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
++ glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
++ } else {
++ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
++ }
++ glBindTexture(GL_TEXTURE_2D, 0);
++ return textureId;
++ }
++
++ GLuint TextureHelper::createSelectionTexture(const QSize &size, GLuint &frameBuffer,
++ GLuint &depthBuffer)
++ {
++ GLuint textureid;
++
++ // Create texture for the selection buffer
++ glGenTextures(1, &textureid);
++ glBindTexture(GL_TEXTURE_2D, textureid);
++ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
++ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
++ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0, GL_RGBA,
++ GL_UNSIGNED_BYTE, NULL);
++ glBindTexture(GL_TEXTURE_2D, 0);
++
++ // Create render buffer
++ if (depthBuffer)
++ glDeleteRenderbuffers(1, &depthBuffer);
++
++ glGenRenderbuffers(1, &depthBuffer);
++ glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
++ GLenum status = glGetError();
++ // glGetError docs advise to call glGetError in loop to clear all error flags
++ while (status)
++ status = glGetError();
++ if (Utils::isOpenGLES())
++ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size.width(), size.height());
++ else
++ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.width(), size.height());
++
++ status = glGetError();
++ if (status) {
++ qCritical() << "Selection texture render buffer creation failed:" << status;
++ glDeleteTextures(1, &textureid);
++ glBindRenderbuffer(GL_RENDERBUFFER, 0);
++ return 0;
++ }
++ glBindRenderbuffer(GL_RENDERBUFFER, 0);
++
++ // Create frame buffer
++ if (!frameBuffer)
++ glGenFramebuffers(1, &frameBuffer);
++ glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
++
++ // Attach texture to color attachment
++ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureid, 0);
++ // Attach renderbuffer to depth attachment
++ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer);
++
++ // Verify that the frame buffer is complete
++ status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
++ if (status != GL_FRAMEBUFFER_COMPLETE) {
++ qCritical() << "Selection texture frame buffer creation failed:" << status;
++ glDeleteTextures(1, &textureid);
++ textureid = 0;
++ }
++
++ // Restore the default framebuffer
++ glBindFramebuffer(GL_FRAMEBUFFER, 0);
++
++ return textureid;
++ }
++
++ GLuint TextureHelper::createCursorPositionTexture(const QSize &size, GLuint &frameBuffer)
++ {
++ GLuint textureid;
++ glGenTextures(1, &textureid);
++ glBindTexture(GL_TEXTURE_2D, textureid);
++ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0, GL_RGBA,
++ GL_UNSIGNED_BYTE, NULL);
++ glBindTexture(GL_TEXTURE_2D, 0);
++
++ glGenFramebuffers(1, &frameBuffer);
++ glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
++ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
++ textureid, 0);
++
++ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
++ if (status != GL_FRAMEBUFFER_COMPLETE) {
++ qCritical() << "Cursor position mapper frame buffer creation failed:" << status;
++ glDeleteTextures(1, &textureid);
++ textureid = 0;
++ }
++ glBindFramebuffer(GL_FRAMEBUFFER, 0);
++
++ return textureid;
++ }
++
++ GLuint TextureHelper::createUniformTexture(const QColor &color)
++ {
++ QImage image(QSize(int(uniformTextureWidth), int(uniformTextureHeight)),
++ QImage::Format_RGB32);
++ QPainter pmp(&image);
++ pmp.setBrush(QBrush(color));
++ pmp.setPen(Qt::NoPen);
++ pmp.drawRect(0, 0, int(uniformTextureWidth), int(uniformTextureHeight));
++
++ return create2DTexture(image, false, true, false, true);
++ }
++
++ GLuint TextureHelper::createGradientTexture(const QLinearGradient &gradient)
++ {
++ QImage image(QSize(int(gradientTextureWidth), int(gradientTextureHeight)),
++ QImage::Format_RGB32);
++ QPainter pmp(&image);
++ pmp.setBrush(QBrush(gradient));
++ pmp.setPen(Qt::NoPen);
++ pmp.drawRect(0, 0, int(gradientTextureWidth), int(gradientTextureHeight));
++
++ return create2DTexture(image, false, true, false, true);
++ }
++
++ GLuint TextureHelper::createDepthTexture(const QSize &size, GLuint textureSize)
++ {
++ GLuint depthtextureid = 0;
++ #if QT_CONFIG(opengles2)
++ Q_UNUSED(size);
++ Q_UNUSED(textureSize);
++ #else
++ if (!Utils::isOpenGLES()) {
++ // Create depth texture for the shadow mapping
++ glGenTextures(1, &depthtextureid);
++ glBindTexture(GL_TEXTURE_2D, depthtextureid);
++ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
++ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
++ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
++ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
++ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
++ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
++ glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, size.width() * textureSize,
++ size.height() * textureSize, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
++ glBindTexture(GL_TEXTURE_2D, 0);
++ }
++ #endif
++ return depthtextureid;
++ }
++
++ GLuint TextureHelper::createDepthTextureFrameBuffer(const QSize &size, GLuint &frameBuffer,
++ GLuint textureSize)
++ {
++ GLuint depthtextureid = createDepthTexture(size, textureSize);
++ #if QT_CONFIG(opengles2)
++ Q_UNUSED(frameBuffer);
++ #else
++ if (!Utils::isOpenGLES()) {
++ // Create frame buffer
++ if (!frameBuffer)
++ glGenFramebuffers(1, &frameBuffer);
++ glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
++
++ // Attach texture to depth attachment
++ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthtextureid, 0);
++
++ m_openGlFunctions_2_1->glDrawBuffers(0, GL_NONE);
++ m_openGlFunctions_2_1->glReadBuffer(GL_NONE);
++
++ // Verify that the frame buffer is complete
++ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
++ if (status != GL_FRAMEBUFFER_COMPLETE) {
++ qCritical() << "Depth texture frame buffer creation failed" << status;
++ glDeleteTextures(1, &depthtextureid);
++ depthtextureid = 0;
++ }
++
++ // Restore the default framebuffer
++ glBindFramebuffer(GL_FRAMEBUFFER, 0);
++ }
++ #endif
++ return depthtextureid;
++ }
++
++ void TextureHelper::deleteTexture(GLuint *texture)
++ {
++ if (texture && *texture) {
++ if (QOpenGLContext::currentContext())
++ glDeleteTextures(1, texture);
++ *texture = 0;
++ }
++ }
++
++ QImage TextureHelper::convertToGLFormat(const QImage &srcImage)
++ {
++ QImage res(srcImage.size(), QImage::Format_ARGB32);
++ convertToGLFormatHelper(res, srcImage.convertToFormat(QImage::Format_ARGB32), GL_RGBA);
++ return res;
++ }
++
++ void TextureHelper::convertToGLFormatHelper(QImage &dstImage, const QImage &srcImage,
++ GLenum texture_format)
++ {
++ Q_ASSERT(dstImage.depth() == 32);
++ Q_ASSERT(srcImage.depth() == 32);
++
++ if (dstImage.size() != srcImage.size()) {
++ int target_width = dstImage.width();
++ int target_height = dstImage.height();
++ float sx = target_width / float(srcImage.width());
++ float sy = target_height / float(srcImage.height());
++
++ quint32 *dest = (quint32 *) dstImage.scanLine(0); // NB! avoid detach here
++ uchar *srcPixels = (uchar *) srcImage.scanLine(srcImage.height() - 1);
++ int sbpl = srcImage.bytesPerLine();
++ int dbpl = dstImage.bytesPerLine();
++
++ int ix = int(0x00010000 / sx);
++ int iy = int(0x00010000 / sy);
++
++ quint32 basex = int(0.5 * ix);
++ quint32 srcy = int(0.5 * iy);
++
++ // scale, swizzle and mirror in one loop
++ while (target_height--) {
++ const uint *src = (const quint32 *) (srcPixels - (srcy >> 16) * sbpl);
++ int srcx = basex;
++ for (int x=0; x<target_width; ++x) {
++ dest[x] = qt_gl_convertToGLFormatHelper(src[srcx >> 16], texture_format);
++ srcx += ix;
++ }
++ dest = (quint32 *)(((uchar *) dest) + dbpl);
++ srcy += iy;
++ }
++ } else {
++ const int width = srcImage.width();
++ const int height = srcImage.height();
++ const uint *p = (const uint*) srcImage.scanLine(srcImage.height() - 1);
++ uint *q = (uint*) dstImage.scanLine(0);
++
++ #if !QT_CONFIG(opengles2)
++ if (texture_format == GL_BGRA) {
++ #else
++ if (texture_format == GL_BGRA8_EXT) {
++ #endif
++ if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
++ // mirror + swizzle
++ for (int i=0; i < height; ++i) {
++ const uint *end = p + width;
++ while (p < end) {
++ *q = ((*p << 24) & 0xff000000)
++ | ((*p >> 24) & 0x000000ff)
++ | ((*p << 8) & 0x00ff0000)
++ | ((*p >> 8) & 0x0000ff00);
++ p++;
++ q++;
++ }
++ p -= 2 * width;
++ }
++ } else {
++ const uint bytesPerLine = srcImage.bytesPerLine();
++ for (int i=0; i < height; ++i) {
++ memcpy(q, p, bytesPerLine);
++ q += width;
++ p -= width;
++ }
++ }
++ } else {
++ if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
++ for (int i=0; i < height; ++i) {
++ const uint *end = p + width;
++ while (p < end) {
++ *q = (*p << 8) | ((*p >> 24) & 0xff);
++ p++;
++ q++;
++ }
++ p -= 2 * width;
++ }
++ } else {
++ for (int i=0; i < height; ++i) {
++ const uint *end = p + width;
++ while (p < end) {
++ *q = ((*p << 16) & 0xff0000) | ((*p >> 16) & 0xff) | (*p & 0xff00ff00);
++ p++;
++ q++;
++ }
++ p -= 2 * width;
++ }
++ }
++ }
++ }
++ }
++
++ QRgb TextureHelper::qt_gl_convertToGLFormatHelper(QRgb src_pixel, GLenum texture_format)
++ {
++ #if !QT_CONFIG(opengles2)
++ if (texture_format == GL_BGRA) {
++ #else
++ if (texture_format == GL_BGRA8_EXT) {
++ #endif
++ if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
++ return ((src_pixel << 24) & 0xff000000)
++ | ((src_pixel >> 24) & 0x000000ff)
++ | ((src_pixel << 8) & 0x00ff0000)
++ | ((src_pixel >> 8) & 0x0000ff00);
++ } else {
++ return src_pixel;
++ }
++ } else { // GL_RGBA
++ if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
++ return (src_pixel << 8) | ((src_pixel >> 24) & 0xff);
++ } else {
++ return ((src_pixel << 16) & 0xff0000)
++ | ((src_pixel >> 16) & 0xff)
++ | (src_pixel & 0xff00ff00);
++ }
++ }
++ }
++
++ QT_END_NAMESPACE