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