From f099a82eb2b2a6bb64cd51d98ab027f4ce45078c Mon Sep 17 00:00:00 2001 From: hachem Date: Sun, 30 Aug 2026 17:59:14 +0200 Subject: feat: little pepe --- src/ui/image_renderer.cpp | 186 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 src/ui/image_renderer.cpp (limited to 'src/ui/image_renderer.cpp') diff --git a/src/ui/image_renderer.cpp b/src/ui/image_renderer.cpp new file mode 100644 index 0000000..8cca305 --- /dev/null +++ b/src/ui/image_renderer.cpp @@ -0,0 +1,186 @@ +#include "ui/image_renderer.h" + +#include "core/log.h" + +#include + +#include +#include +#include +#include + +#ifndef HSDBG_ASSET_DIR + #define HSDBG_ASSET_DIR "" +#endif + +namespace Hsdbg +{ + namespace + { + auto read_file(const std::filesystem::path& path) -> std::string + { + std::ifstream file(path); + + if (!file.is_open()) + { + Log::error("image_renderer: could not open '{}'", path.string()); + return {}; + } + + return std::string(std::istreambuf_iterator(file), + std::istreambuf_iterator()); + } + + auto compile(unsigned int type, const char* source) -> unsigned int + { + const unsigned int shader = glCreateShader(type); + glShaderSource(shader, 1, &source, nullptr); + glCompileShader(shader); + + int ok = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &ok); + + if (ok == 0) + { + char log[512]; + glGetShaderInfoLog(shader, sizeof(log), nullptr, log); + Log::error("image_renderer: shader compile failed: {}", log); + glDeleteShader(shader); + return 0; + } + + return shader; + } + } + + ImageRenderer::~ImageRenderer() + { + shutdown(); + } + + auto ImageRenderer::init() -> bool + { + const std::filesystem::path assets(HSDBG_ASSET_DIR); + const std::string vertex_source = read_file(assets / "texture.vert"); + const std::string fragment_source = read_file(assets / "texture.glsl"); + + if (vertex_source.empty() || fragment_source.empty()) + return false; + + const unsigned int vertex = compile(GL_VERTEX_SHADER, vertex_source.c_str()); + const unsigned int fragment = compile(GL_FRAGMENT_SHADER, fragment_source.c_str()); + + if (vertex == 0 || fragment == 0) + return false; + + m_program = glCreateProgram(); + glAttachShader(m_program, vertex); + glAttachShader(m_program, fragment); + glBindAttribLocation(m_program, 0, "a_position"); + glBindAttribLocation(m_program, 1, "a_uv"); + glLinkProgram(m_program); + glDeleteShader(vertex); + glDeleteShader(fragment); + + int ok = 0; + glGetProgramiv(m_program, GL_LINK_STATUS, &ok); + + if (ok == 0) + { + char log[512]; + glGetProgramInfoLog(m_program, sizeof(log), nullptr, log); + Log::error("image_renderer: program link failed: {}", log); + glDeleteProgram(m_program); + m_program = 0; + return false; + } + + m_texture_uniform = glGetUniformLocation(m_program, "u_texture"); + + glGenVertexArrays(1, &m_vao); + glGenBuffers(1, &m_vbo); + + glBindVertexArray(m_vao); + glBindBuffer(GL_ARRAY_BUFFER, m_vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 24, nullptr, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, + reinterpret_cast(0)); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, + reinterpret_cast(sizeof(float) * 2)); + glBindVertexArray(0); + + return true; + } + + auto ImageRenderer::shutdown() -> void + { + if (m_vbo != 0) + glDeleteBuffers(1, &m_vbo); + + if (m_vao != 0) + glDeleteVertexArrays(1, &m_vao); + + if (m_program != 0) + glDeleteProgram(m_program); + + m_vbo = 0; + m_vao = 0; + m_program = 0; + } + + auto ImageRenderer::draw(unsigned int texture, + float x0, float y0, float x1, float y1, + int framebuffer_width, int framebuffer_height) -> void + { + if (m_program == 0 || texture == 0 || framebuffer_width <= 0 || framebuffer_height <= 0) + return; + + const auto to_ndc_x = [&](float pixel) { + return pixel / static_cast(framebuffer_width) * 2.0f - 1.0f; + }; + const auto to_ndc_y = [&](float pixel) { + return 1.0f - pixel / static_cast(framebuffer_height) * 2.0f; + }; + + const float left = to_ndc_x(x0); + const float right = to_ndc_x(x1); + const float top = to_ndc_y(y0); + const float bottom = to_ndc_y(y1); + + // two triangles; the u coordinate runs 1 -> 0 left-to-right so the image + // is mirrored horizontally, and v (0,0) stays at the top to match how the + // texture was uploaded (stb hands back the first row as the top) + const float vertices[24] = { + left, top, 1.0f, 0.0f, + left, bottom, 1.0f, 1.0f, + right, bottom, 0.0f, 1.0f, + left, top, 1.0f, 0.0f, + right, bottom, 0.0f, 1.0f, + right, top, 0.0f, 0.0f, + }; + + glViewport(0, 0, framebuffer_width, framebuffer_height); + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_CULL_FACE); + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + glUseProgram(m_program); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture); + glUniform1i(m_texture_uniform, 0); + + glBindVertexArray(m_vao); + glBindBuffer(GL_ARRAY_BUFFER, m_vbo); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); + glDrawArrays(GL_TRIANGLES, 0, 6); + + glBindVertexArray(0); + glBindTexture(GL_TEXTURE_2D, 0); + glUseProgram(0); + } +} -- cgit v1.3