#include #include #include #include "OpenGLHandler.h" OpenGLHandler::OpenGLHandler() : _texture(QOpenGLTexture::Target2D) { if(!_context.create()) { qDebug() << "Can't create GL _context."; return; } _surface.setFormat(_context.format()); _surface.create(); if(!_surface.isValid()) { qDebug() << "_surface not valid."; return; } if(!_context.makeCurrent(&_surface)) { qDebug() << "Can't make _context current."; return; } _image = new QImage("/home/mmt/Desktop/Projects/OpenGLOffscreen/sono.jpg"); _fbo = new QOpenGLFramebufferObject(_image->size()); _context.functions()->glViewport(0, 0, _image->width(), _image->height()); fun = new QOpenGLFunctions_3_3_Core(); if(!fun->initializeOpenGLFunctions()) { qDebug() << "Can't init GL functions."; return; } _shader = new QOpenGLShaderProgram(&_context); loadChars(); } bool OpenGLHandler::prepareShader(QString vertexShader, QString fragmentShader) { if(!_shader->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexShader)) { qDebug() << "Can't add vertex shader."; return false; } if(!_shader->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShader)) { qDebug() << "Can't add fragment shader."; return {}; } if(!_shader->link()) { qDebug() << "Can't link program."; return false; } if(!_shader->bind()) { qDebug() << "Can't bind program."; return false; } return true; } bool OpenGLHandler::prepareTexture(const QImage& image) { _texture.setData(image); _texture.bind(); if(!_texture.isBound()) { qDebug() << "Texture not bound."; return false; } return true; } bool OpenGLHandler::prepareCoords(QOpenGLBuffer vertexBuf, QOpenGLBuffer indexBuf) { VertexData vertices[] = { {{ -0.5f, +0.5f }, { 0.0f, 0.0f }}, //top-left {{ +0.5f, +0.5f }, { 1.0f, 0.0f }}, //top-right {{ -0.5f, -0.5f }, { 0.0f, 1.0f }}, //bottom-left {{ +0.5f, -0.5f }, { 1.0f, 1.0f }} //bottom-right //{{ -0.5f, +0.5f }, { 0.0f, 0.0f }}, //top-left //{{ +0.5f, +0.5f }, { 1.0f, 0.0f }}, //top-right //{{ -0.5f, -0.5f }, { 0.0f, 1.0f }}, //bottom-left //{{ +0.5f, -0.5f }, { 1.0f, 1.0f }} //bottom-right }; GLuint indices[] = { 0, 1, 2, 3 }; if(!vertexBuf.create()) { qDebug() << "Can't create vertex buffer."; return {}; } if(!indexBuf.create()) { qDebug() << "Can't create index buffer."; return {}; } if(!vertexBuf.bind()) { qDebug() << "Can't bind vertex buffer."; return {}; } vertexBuf.allocate(vertices, 4 * sizeof(VertexData)); if(!indexBuf.bind()) { qDebug() << "Can't bind index buffer."; return {}; } indexBuf.allocate(indices, 4 * sizeof(GLuint)); return true; } void OpenGLHandler::loadChars() { if(FT_Init_FreeType(&_ft)) { qDebug() << "ERROR::FREETYPE: Could not init FreeType Library"; } if(FT_New_Face(_ft, "/home/mmt/.fonts/Roboto-Black.ttf", 0, &_face)) { qDebug() << "ERROR::FREETYPE: Failed to load font"; } FT_Set_Pixel_Sizes(_face, 0, 48); if(FT_Load_Char(_face, 'X', FT_LOAD_RENDER)) { qDebug() << "ERROR::FREETYTPE: Failed to load Glyph"; } glPixelStorei(GL_UNPACK_ALIGNMENT, 1); //disable byte-alignment restriction int i = 0; for(unsigned char c = 0; c < 128; c++) { //load character glyph if(FT_Load_Char(_face, c, FT_LOAD_RENDER)) { qDebug() << "ERROR::FREETYTPE: Failed to load Glyph\n"; continue; } //// generate texture unsigned int texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexImage2D( GL_TEXTURE_2D, 0, GL_RED, _face->glyph->bitmap.width, _face->glyph->bitmap.rows, 0, GL_RED, GL_UNSIGNED_BYTE, _face->glyph->bitmap.buffer ); //qDebug() << _face->glyph->bitmap.palette_mode; //QImage chImage(_face->glyph->bitmap.buffer, _face->glyph->bitmap.width, //_face->glyph->bitmap.rows, //QImage::Format::Format_Grayscale8); //chImage.save("/home/mmt/Desktop/chr/" + static_cast(i++) + ".png"); //// set texture options 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_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //now store character for later use Character character = { texture, glm::ivec2(_face->glyph->bitmap.width, _face->glyph->bitmap.rows), glm::ivec2(_face->glyph->bitmap_left, _face->glyph->bitmap_top), _face->glyph->advance.x }; _characters.insert(c, character); } glBindTexture(GL_TEXTURE_2D, 0); FT_Done_Face(_face); FT_Done_FreeType(_ft); } void OpenGLHandler::processText() { QString vertexShadre = "#version 330 core\n" "layout (location = 4) in vec4 vertex;" "out vec2 TexCoords;" "uniform mat4 projection;" "void main()" "{" " gl_Position = projection * vec4(vertex.xy, 0.0, 1.0);" " TexCoords.x = vertex.z;" " TexCoords.y = vertex.w;" "}"; QString fragShadre = "#version 330 core\n" "in vec2 TexCoords;" "out vec4 color;" "uniform sampler2D text;" "uniform vec3 textColor;" "void main()" "{" " vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);" " color = vec4(textColor, 1.00) * sampled;" "}"; prepareShader(vertexShadre, fragShadre); //_shader->link(); //_shader->bind(); //fun->glEnable(GL_CULL_FACE); fun->glEnable(GL_BLEND); //glBlendFunc(GL_ONE, GL_ZERO); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); QMatrix4x4 qProjection; QRect temp(0.0f, static_cast(320), 0.0f, static_cast(480)); qProjection.ortho(temp); _shader->setUniformValueArray(_shader->uniformLocation("projection"), &qProjection, 1); fun->glGenVertexArrays(1, &chVAO); fun->glGenBuffers(1, &chVBO); fun->glBindVertexArray(chVAO); fun->glBindBuffer(GL_ARRAY_BUFFER, chVBO); fun->glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, NULL, GL_DYNAMIC_DRAW); fun->glEnableVertexAttribArray(4); fun->glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0); fun->glBindBuffer(GL_ARRAY_BUFFER, 0); fun->glBindVertexArray(0); RenderText("This is sample text", -0.8f, -.8f, .003f, glm::vec3(0.5, 0.8f, 0.2f)); RenderText("(C) LearnOpenGL.com", -0.5f, 0.00f, 0.005f, glm::vec3(0.3, 0.7f, 0.9f)); //glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) //------------------------------------------------------------------------------- //glfwSwapBuffers(_window); //glfwPollEvents(); auto log = _shader->log(); if(log != "") { qDebug() << "log : " << log; } fun->glDeleteVertexArrays(1, &chVAO); fun->glDeleteBuffers(1, &chVBO); _shader->removeAllShaders(); fun->glDisable(GL_BLEND); glDisable(GL_CULL_FACE); } void OpenGLHandler::RenderText(std::string text, float x, float y, float scale, glm::vec3 color) { //activate corresponding render state _shader->setUniformValue(_shader->uniformLocation("textColor"), color.x, color.y, color.z); glActiveTexture(GL_TEXTURE0); fun->glBindVertexArray(chVAO); //iterate through all characters std::string::const_iterator c; for(c = text.begin(); c != text.end(); c++) { //qDebug() << "here"; Character ch = _characters[*c]; float xpos = x + ch.Bearing.x * scale; float ypos = y - (ch.Size.y - ch.Bearing.y) * scale; //qDebug() << "xpos = " << xpos; float w = ch.Size.x * scale; float h = ch.Size.y * scale; //qDebug() << ypos + h; //update VBO for each character float vertices[6][4] = { { xpos, ypos + h, 0.0f, 0.0f }, { xpos, ypos, 0.0f, 1.0f }, { xpos + w, ypos, 1.0f, 1.0f }, { xpos, ypos + h, 0.0f, 0.0f }, { xpos + w, ypos, 1.0f, 1.0f }, { xpos + w, ypos + h, 1.0f, 0.0f } }; //render glyph texture over quad glBindTexture(GL_TEXTURE_2D, ch.TextureID); //update content of VBO memory fun->glBindBuffer(GL_ARRAY_BUFFER, chVBO); fun->glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); //be sure to use //glBufferSubData and not //glBufferData fun->glBindBuffer(GL_ARRAY_BUFFER, 0); //render quad glDrawArrays(GL_TRIANGLES, 0, 6); //now advance cursors for next glyph (note that advance is number of 1/64 pixels) x += (ch.Advance >> 6) * scale; //bitshift by 6 to get value in pixels (2^6 = 64 (divide //amount of 1/64th pixels by 64 to get amount of pixels)) } fun->glBindVertexArray(0); glBindTexture(GL_TEXTURE_2D, 0); } bool OpenGLHandler::prepareCoords2(QOpenGLBuffer vertexBuf, QOpenGLBuffer indexBuf) { VertexData vertices[] = { {{ -1.0f, +1.0f }, { 0.0f, 1.0f }}, //top-left {{ +1.0f, +1.0f }, { 1.0f, 1.0f }}, //top-right {{ -1.0f, -1.0f }, { 0.0f, 0.0f }}, //bottom-left {{ +1.0f, -1.0f }, { 1.0f, 0.0f }} //bottom-right }; GLuint indices[] = { 0, 1, 2, 3 }; if(!vertexBuf.create()) { qDebug() << "Can't create vertex buffer."; return {}; } if(!indexBuf.create()) { qDebug() << "Can't create index buffer."; return {}; } if(!vertexBuf.bind()) { qDebug() << "Can't bind vertex buffer."; return {}; } vertexBuf.allocate(vertices, 4 * sizeof(VertexData)); if(!indexBuf.bind()) { qDebug() << "Can't bind index buffer."; return {}; } indexBuf.allocate(indices, 4 * sizeof(GLuint)); return true; } QString OpenGLHandler::getImage(QImage img) { QByteArray byteArray; QBuffer buffer(&byteArray); buffer.open(QIODevice::WriteOnly); img.save(&buffer, "png"); //save image data in string QString image("data:image/png;base64,"); image.append(QString::fromLatin1(byteArray.toBase64().data())); return image; } OpenGLHandler::~OpenGLHandler() { delete _image; delete _fbo; delete _shader; } void OpenGLHandler::processImage(const QImage& image, const QString& vertexShader, const QString& fragmentShader, const QString& textureVar, const QString& vertexPosVar, const QString& textureCoordVar) { //fun->glEnable(GL_BLEND); bool a; QOpenGLBuffer vertexBuf(QOpenGLBuffer::VertexBuffer); QOpenGLBuffer indexBuf(QOpenGLBuffer::IndexBuffer); a = prepareShader(vertexShader, fragmentShader); a = prepareTexture(image); a = prepareCoords(vertexBuf, indexBuf); int offset = 0; _shader->enableAttributeArray(vertexPosVar.toLatin1().data()); _shader->setAttributeBuffer(vertexPosVar.toLatin1().data(), GL_FLOAT, offset, 2, sizeof(VertexData)); offset += sizeof(QVector2D); _shader->enableAttributeArray(textureCoordVar.toLatin1().data()); _shader->setAttributeBuffer(textureCoordVar.toLatin1().data(), GL_FLOAT, offset, 2, sizeof(VertexData)); _shader->setUniformValue(textureVar.toLatin1().data(), 0); fun->glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, Q_NULLPTR); _shader->disableAttributeArray(vertexPosVar.toLatin1().data()); _shader->disableAttributeArray(textureCoordVar.toLatin1().data()); _shader->disableAttributeArray(textureVar.toLatin1().data()); _shader->removeAllShaders(); } void OpenGLHandler::flipImage() { QString textureVar = "texture"; QString vertexPosVar = "aPosition"; QString textureCoordVar = "aTexCoord"; QString flipVertexShader = "attribute vec4 aPosition;\n" "attribute vec2 aTexCoord;\n" "varying vec2 vTexCoord;\n" "void main()\n" "{\n" " gl_Position = aPosition;\n" " vTexCoord.x = -aTexCoord.x;\n" " vTexCoord.y = aTexCoord.y;\n" "}"; QString flipFragmentShader = "uniform sampler2D texture;\n" "varying vec2 vTexCoord;\n" "void main()\n" "{\n" " gl_FragColor = texture2D(texture, vTexCoord);\n" "}"; prepareShader(flipVertexShader, ""); QOpenGLBuffer vertexBuf(QOpenGLBuffer::VertexBuffer); QOpenGLBuffer indexBuf(QOpenGLBuffer::IndexBuffer); prepareCoords2(vertexBuf, indexBuf); int offset = 0; _shader->enableAttributeArray(vertexPosVar.toLatin1().data()); _shader->setAttributeBuffer(vertexPosVar.toLatin1().data(), GL_FLOAT, offset, 2, sizeof(VertexData)); offset += sizeof(QVector2D); _shader->enableAttributeArray(textureCoordVar.toLatin1().data()); _shader->setAttributeBuffer(textureCoordVar.toLatin1().data(), GL_FLOAT, offset, 2, sizeof(VertexData)); _shader->setUniformValue(textureVar.toLatin1().data(), 0); fun->glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, Q_NULLPTR); _shader->disableAttributeArray(vertexPosVar.toLatin1().data()); _shader->disableAttributeArray(textureCoordVar.toLatin1().data()); _shader->disableAttributeArray(textureVar.toLatin1().data()); _shader->removeAllShaders(); } void OpenGLHandler::run() { //fun->glClearColor(0, 1, 1, 1); //fun->glClear(GL_COLOR_BUFFER_BIT); processImage(*_image, vertexShader, fragmentShader, "texture", "aPosition", "aTexCoord"); processText(); //flipImage(); auto ret = _fbo->toImage(true); //auto address = getImage(ret); ret.save("/home/mmt/Desktop/test.png"); }