File indexing completed on 2024-05-26 04:32:41
0001 /* 0002 * SPDX-FileCopyrightText: 2019 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "KisGLImageWidget.h" 0008 0009 #include <QPainter> 0010 #include <QFile> 0011 #include <QResizeEvent> 0012 #include "kis_debug.h" 0013 #include <config-hdr.h> 0014 #include <opengl/kis_opengl.h> 0015 0016 #include "KisGLImageF16.h" 0017 0018 namespace { 0019 inline void rectToVertices(QVector3D* vertices, const QRectF &rc) 0020 { 0021 vertices[0] = QVector3D(rc.left(), rc.bottom(), 0.f); 0022 vertices[1] = QVector3D(rc.left(), rc.top(), 0.f); 0023 vertices[2] = QVector3D(rc.right(), rc.bottom(), 0.f); 0024 vertices[3] = QVector3D(rc.left(), rc.top(), 0.f); 0025 vertices[4] = QVector3D(rc.right(), rc.top(), 0.f); 0026 vertices[5] = QVector3D(rc.right(), rc.bottom(), 0.f); 0027 } 0028 0029 inline void rectToTexCoords(QVector2D* texCoords, const QRectF &rc) 0030 { 0031 texCoords[0] = QVector2D(rc.left(), rc.bottom()); 0032 texCoords[1] = QVector2D(rc.left(), rc.top()); 0033 texCoords[2] = QVector2D(rc.right(), rc.bottom()); 0034 texCoords[3] = QVector2D(rc.left(), rc.top()); 0035 texCoords[4] = QVector2D(rc.right(), rc.top()); 0036 texCoords[5] = QVector2D(rc.right(), rc.bottom()); 0037 } 0038 } 0039 0040 KisGLImageWidget::KisGLImageWidget(QWidget *parent) 0041 : KisGLImageWidget(KisSurfaceColorSpace::sRGBColorSpace, parent) 0042 { 0043 } 0044 0045 KisGLImageWidget::KisGLImageWidget(KisSurfaceColorSpace colorSpace, 0046 QWidget *parent) 0047 : QOpenGLWidget(parent), 0048 m_texture(QOpenGLTexture::Target2D) 0049 { 0050 Q_UNUSED(colorSpace); 0051 0052 #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) 0053 setTextureFormat(GL_RGBA16F); 0054 #endif 0055 0056 #ifdef HAVE_HDR 0057 setTextureColorSpace(colorSpace); 0058 #endif 0059 0060 setUpdateBehavior(QOpenGLWidget::NoPartialUpdate); 0061 } 0062 0063 KisGLImageWidget::~KisGLImageWidget() 0064 { 0065 // force releasing the resources on destruction 0066 slotOpenGLContextDestroyed(); 0067 } 0068 0069 void KisGLImageWidget::initializeGL() 0070 { 0071 initializeOpenGLFunctions(); 0072 0073 connect(context(), SIGNAL(aboutToBeDestroyed()), SLOT(slotOpenGLContextDestroyed())); 0074 m_shader.reset(new QOpenGLShaderProgram); 0075 0076 QFile vertexShaderFile(QString(":/") + "kis_gl_image_widget.vert"); 0077 vertexShaderFile.open(QIODevice::ReadOnly); 0078 QString vertSource = vertexShaderFile.readAll(); 0079 0080 QFile fragShaderFile(QString(":/") + "kis_gl_image_widget.frag"); 0081 fragShaderFile.open(QIODevice::ReadOnly); 0082 QString fragSource = fragShaderFile.readAll(); 0083 0084 if (context()->isOpenGLES()) { 0085 const char *versionHelper = "#define USE_OPENGLES\n"; 0086 vertSource.prepend(versionHelper); 0087 fragSource.prepend(versionHelper); 0088 0089 const char *versionDefinition = "#version 100\n"; 0090 vertSource.prepend(versionDefinition); 0091 fragSource.prepend(versionDefinition); 0092 } else { 0093 #ifdef Q_OS_MACOS 0094 const char *versionDefinition = KisOpenGL::supportsLoD() ? "#version 150\n" : "#version 120\n"; 0095 #else 0096 const char *versionDefinition = KisOpenGL::supportsLoD() ? "#version 130\n" : "#version 120\n"; 0097 #endif 0098 vertSource.prepend(versionDefinition); 0099 fragSource.prepend(versionDefinition); 0100 } 0101 0102 if (!m_shader->addShaderFromSourceCode(QOpenGLShader::Vertex, vertSource)) { 0103 qDebug() << "Could not add vertex code"; 0104 return; 0105 } 0106 0107 if (!m_shader->addShaderFromSourceCode(QOpenGLShader::Fragment, fragSource)) { 0108 qDebug() << "Could not add fragment code"; 0109 return; 0110 } 0111 0112 if (!m_shader->link()) { 0113 qDebug() << "Could not link"; 0114 return; 0115 } 0116 0117 if (!m_shader->bind()) { 0118 qDebug() << "Could not bind"; 0119 return; 0120 } 0121 0122 m_shader->release(); 0123 0124 0125 m_vao.create(); 0126 m_vao.bind(); 0127 0128 m_verticesBuffer.create(); 0129 updateVerticesBuffer(this->rect()); 0130 0131 QVector<QVector2D> textureVertices(6); 0132 rectToTexCoords(textureVertices.data(), QRect(0.0, 0.0, 1.0, 1.0)); 0133 0134 m_textureVerticesBuffer.create(); 0135 m_textureVerticesBuffer.bind(); 0136 m_textureVerticesBuffer.setUsagePattern(QOpenGLBuffer::DynamicDraw); 0137 m_textureVerticesBuffer.allocate(2 * 3 * sizeof(QVector2D)); 0138 m_verticesBuffer.write(0, textureVertices.data(), m_textureVerticesBuffer.size()); 0139 m_textureVerticesBuffer.release(); 0140 0141 m_vao.release(); 0142 0143 0144 if (!m_sourceImage.isNull()) { 0145 loadImage(m_sourceImage); 0146 } 0147 } 0148 0149 void KisGLImageWidget::slotOpenGLContextDestroyed() 0150 { 0151 this->makeCurrent(); 0152 0153 m_shader.reset(); 0154 m_texture.destroy(); 0155 m_verticesBuffer.destroy(); 0156 m_textureVerticesBuffer.destroy(); 0157 m_vao.destroy(); 0158 m_havePendingTextureUpdate = false; 0159 0160 this->doneCurrent(); 0161 } 0162 0163 void KisGLImageWidget::updateVerticesBuffer(const QRect &rect) 0164 { 0165 if (!m_vao.isCreated() || !m_verticesBuffer.isCreated()) return; 0166 0167 QVector<QVector3D> vertices(6); 0168 rectToVertices(vertices.data(), rect); 0169 0170 m_verticesBuffer.bind(); 0171 m_verticesBuffer.setUsagePattern(QOpenGLBuffer::DynamicDraw); 0172 m_verticesBuffer.allocate(2 * 3 * sizeof(QVector3D)); 0173 m_verticesBuffer.write(0, vertices.data(), m_verticesBuffer.size()); 0174 m_verticesBuffer.release(); 0175 } 0176 0177 0178 void KisGLImageWidget::paintGL() 0179 { 0180 // TODO: fix conversion to the destination surface space 0181 // Fill with bright color as as default for debugging purposes 0182 // glClearColor(bgColor.redF(), bgColor.greenF(), bgColor.blueF(), 1.0f); 0183 glClearColor(0.3, 0.2, 0.8, 1.0f); 0184 glClear(GL_COLOR_BUFFER_BIT); 0185 0186 0187 0188 if (m_havePendingTextureUpdate) { 0189 m_havePendingTextureUpdate = false; 0190 0191 if (!m_texture.isCreated() || 0192 m_sourceImage.width() != m_texture.width() || 0193 m_sourceImage.height() != m_texture.height()) { 0194 0195 if (m_texture.isCreated()) { 0196 m_texture.destroy(); 0197 } 0198 0199 m_texture.setFormat(QOpenGLTexture::RGBA16F); 0200 m_texture.setSize(m_sourceImage.width(), m_sourceImage.height()); 0201 m_texture.allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::Float16); 0202 m_texture.setMinificationFilter(QOpenGLTexture::LinearMipMapLinear); 0203 m_texture.setMagnificationFilter(QOpenGLTexture::Linear); 0204 m_texture.setWrapMode(QOpenGLTexture::ClampToEdge); 0205 } 0206 0207 m_texture.setData(QOpenGLTexture::RGBA, QOpenGLTexture::Float16, m_sourceImage.constData()); 0208 } 0209 0210 if (!m_texture.isCreated()) return; 0211 0212 m_vao.bind(); 0213 m_shader->bind(); 0214 0215 { 0216 QMatrix4x4 projectionMatrix; 0217 projectionMatrix.setToIdentity(); 0218 projectionMatrix.ortho(0, width(), height(), 0, -1, 1); 0219 QMatrix4x4 viewProjectionMatrix; 0220 0221 // use a QTransform to scale, translate, rotate your view 0222 QTransform transform; // TODO: noop! 0223 viewProjectionMatrix = projectionMatrix * QMatrix4x4(transform); 0224 0225 m_shader->setUniformValue("viewProjectionMatrix", viewProjectionMatrix); 0226 } 0227 0228 m_shader->enableAttributeArray("vertexPosition"); 0229 m_verticesBuffer.bind(); 0230 m_shader->setAttributeBuffer("vertexPosition", GL_FLOAT, 0, 3); 0231 0232 m_shader->enableAttributeArray("texturePosition"); 0233 m_textureVerticesBuffer.bind(); 0234 m_shader->setAttributeBuffer("texturePosition", GL_FLOAT, 0, 2); 0235 0236 glActiveTexture(GL_TEXTURE0); 0237 m_texture.bind(); 0238 0239 // draw 2 triangles = 6 vertices starting at offset 0 in the buffer 0240 glDrawArrays(GL_TRIANGLES, 0, 6); 0241 0242 m_verticesBuffer.release(); 0243 m_textureVerticesBuffer.release(); 0244 m_texture.release(); 0245 m_shader->release(); 0246 m_vao.release(); 0247 } 0248 0249 void KisGLImageWidget::loadImage(const KisGLImageF16 &image) 0250 { 0251 if (m_sourceImage != image) { 0252 m_sourceImage = image; 0253 } 0254 0255 m_havePendingTextureUpdate = true; 0256 0257 0258 updateGeometry(); 0259 update(); 0260 } 0261 0262 void KisGLImageWidget::paintEvent(QPaintEvent *event) 0263 { 0264 QOpenGLWidget::paintEvent(event); 0265 } 0266 0267 void KisGLImageWidget::resizeEvent(QResizeEvent *event) 0268 { 0269 updateVerticesBuffer(QRect(QPoint(),event->size())); 0270 QOpenGLWidget::resizeEvent(event); 0271 } 0272 0273 QSize KisGLImageWidget::sizeHint() const 0274 { 0275 return m_sourceImage.size(); 0276 } 0277