You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
481 lines
14 KiB
481 lines
14 KiB
#include <QImageReader>
|
|
#include <QOpenGLFunctions>
|
|
#include <QOpenGLBuffer>
|
|
|
|
#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<QString>(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<float>(320), 0.0f, static_cast<float>(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");
|
|
}
|
|
|