#ifndef FREE_GLYPH_H #define FREE_GLYPH_H #include #include #include #include FT_FREETYPE_H #include "vec2.h" #include "vec4.h" #include "glyph_attr.h" struct glyph_info { struct vec2i64 a; struct { uint32_t w; uint32_t h; int32_t l; int32_t t; } b; float tx; }; struct free_glyph { struct vec2f pos; struct vec2f size; struct vec2f uv_pos; struct vec2f uv_size; struct vec4f fg_color; struct vec4f bg_color; }; DA_DEF_STRUCT(struct free_glyph, free_glyph_da); struct free_glyph_render { uint32_t font_tex; struct vec2ui atlas_dim; struct free_glyph_da glyphs; struct { uint32_t vert; uint32_t frag; } shaders; uint32_t prog; struct { int32_t resolution; int32_t time; int32_t camera; } uniforms; uint32_t vao; uint32_t vbo; struct glyph_info g_infos[128]; }; enum free_glyph_attr_enum { FREE_GLYPH_ATTR_POS = 0, FREE_GLYPH_ATTR_SIZE, FREE_GLYPH_ATTR_UV_POS, FREE_GLYPH_ATTR_UV_SIZE, FREE_GLYPH_ATTR_FG_COLOR, FREE_GLYPH_ATTR_BG_COLOR, FREE_GLYPH_ATTR_TOTAL }; struct free_glyph_render free_glyph_render_create(FT_Face font_face, const char *vert_path, const char *frag_path); struct free_glyph_da free_glyph_da_create(void); const struct glyph_attr* free_glyph_get_attrs(void); void free_glyph_render_use(struct free_glyph_render fgr); float free_glyph_render_calc_col(struct free_glyph_render fgr, struct app app, const char *cstr, size_t cstr_size, struct vec2f pos, size_t index); float free_glyph_render_calc_col_buffer(struct free_glyph_render fgr, struct app app); struct free_glyph_render free_glyph_render_calc( struct free_glyph_render fgr, struct app app, const char *cstr, size_t cstr_size, struct vec2f pos, struct vec4f fg_color, struct vec4f bg_color); struct free_glyph_render free_glyph_render_calc_buffer( struct free_glyph_render fgr, struct app app, struct vec2f pos); void free_glyph_render_sync(struct free_glyph_render fgr); struct free_glyph_render free_glyph_render_gen_buffer( struct free_glyph_render fgr); struct free_glyph_render free_glyph_render_load_texture_atlas_or_exit( struct free_glyph_render fgr, FT_Face font_face); struct vec2ui free_glyph_render_calc_atlas_dimensions(FT_Face font_face); #if defined(FREE_GLYPH_IMP) || defined(IMP) #include "shader.h" #include "gl_util.h" struct free_glyph_render free_glyph_render_create(FT_Face font_face, const char *vert_path, const char *frag_path) { struct free_glyph_render fgr = {0}; fgr = free_glyph_render_load_texture_atlas_or_exit(fgr, font_face); fgr.glyphs = free_glyph_da_create(); fgr.shaders.vert = shader_compile_file_or_exit( vert_path, GL_VERTEX_SHADER); fgr.shaders.frag = shader_compile_file_or_exit( frag_path, GL_FRAGMENT_SHADER); fgr.prog = program_link_or_exit(fgr.shaders.vert, fgr.shaders.frag); glUseProgram(fgr.prog); fgr.uniforms.resolution = get_uniform_or_exit(fgr.prog, "resolution"); fgr.uniforms.time = get_uniform_or_exit(fgr.prog, "time"); fgr.uniforms.camera = get_uniform_or_exit(fgr.prog, "camera"); fgr = free_glyph_render_gen_buffer(fgr); return fgr; } struct free_glyph_da free_glyph_da_create(void) { struct free_glyph_da glys; glys.cap = 1024 * 1024; glys.size = 0; glys.items = calloc(glys.cap, sizeof(struct free_glyph)); return glys; } const struct glyph_attr * free_glyph_get_attrs(void) { static const struct glyph_attr free_glyph_attr[FREE_GLYPH_ATTR_TOTAL] = { [FREE_GLYPH_ATTR_POS] = { .size = 2, .type = GL_FLOAT, .norm = GL_FALSE, .stride = sizeof(struct free_glyph), .offset = offsetof(struct free_glyph, pos) }, [FREE_GLYPH_ATTR_SIZE] = { .size = 2, .type = GL_FLOAT, .norm = GL_FALSE, .stride = sizeof(struct free_glyph), .offset = offsetof(struct free_glyph, size) }, [FREE_GLYPH_ATTR_UV_POS] = { .size = 2, .type = GL_FLOAT, .norm = GL_FALSE, .stride = sizeof(struct free_glyph), .offset = offsetof(struct free_glyph, uv_pos) }, [FREE_GLYPH_ATTR_UV_SIZE] = { .size = 2, .type = GL_FLOAT, .norm = GL_FALSE, .stride = sizeof(struct free_glyph), .offset = offsetof(struct free_glyph, uv_size) }, [FREE_GLYPH_ATTR_FG_COLOR] = { .size = 4, .type = GL_FLOAT, .norm = GL_FALSE, .stride = sizeof(struct free_glyph), .offset = offsetof(struct free_glyph, fg_color) }, [FREE_GLYPH_ATTR_BG_COLOR] = { .size = 4, .type = GL_FLOAT, .norm = GL_FALSE, .stride = sizeof(struct free_glyph), .offset = offsetof(struct free_glyph, bg_color) }, }; return free_glyph_attr; } void free_glyph_render_use(struct free_glyph_render fgr) { glBindVertexArray(fgr.vao); glBindBuffer(GL_ARRAY_BUFFER, fgr.vbo); glUseProgram(fgr.prog); } float free_glyph_render_calc_col(struct free_glyph_render fgr, struct app app, const char *cstr, size_t cstr_size, struct vec2f pos, size_t index) { struct vec2f pen = pos; struct vec2f dim = vec2f_from_ui(fgr.atlas_dim); if ( index > cstr_size ) { index = cstr_size - 1; } for ( size_t i = 0; i < index; ++i ) { char c = cstr[i]; if ( c == '\n' ) { pen.x = 0; pen.y -= dim.y; continue; } struct glyph_info gi = fgr.g_infos[(int64_t)c]; if ( c == '\t' ) { c = ' '; gi = fgr.g_infos[(int64_t)c]; gi.a.x *= app.cfg.tab_size; } pen.x += (float) gi.a.x; pen.y += (float) gi.a.y; } return pen.x; } float free_glyph_render_calc_col_buffer(struct free_glyph_render fgr, struct app app) { return free_glyph_render_calc_col(fgr, app, app.buf.data.items, app.buf.data.size, vec2fs(0), app.buf.cur); } struct free_glyph_render free_glyph_render_calc(struct free_glyph_render fgr, struct app app, const char *cstr, size_t cstr_size, struct vec2f pos, struct vec4f fg_color, struct vec4f bg_color) { struct vec2f pen = pos; struct vec2f dim = vec2f_from_ui(fgr.atlas_dim); for ( size_t i = 0; i < cstr_size; ++i ) { char c = cstr[i]; if ( c == '\n' ) { pen.x = 0; pen.y -= dim.y; continue; } struct glyph_info gi = fgr.g_infos[(int64_t)c]; if ( c == '\t' ) { c = ' '; gi = fgr.g_infos[(int64_t)c]; gi.a.x *= app.cfg.tab_size; } float w = (float)gi.b.w; float h = (float)gi.b.h; struct free_glyph gly = { .pos = { pen.x + (float) gi.b.l, pen.y + (float) gi.b.t }, .size = {w, -h}, .uv_pos = { gi.tx, 0.0f }, .uv_size = { w / dim.x, h / dim.y }, .fg_color = fg_color, .bg_color = bg_color, }; pen.x += (float) gi.a.x; pen.y += (float) gi.a.y; DA_APPEND(fgr.glyphs, gly); } return fgr; } struct free_glyph_render free_glyph_render_calc_buffer(struct free_glyph_render fgr, struct app app, struct vec2f size) { return free_glyph_render_calc(fgr, app, app.buf.data.items, app.buf.data.size, size, vec4fs(1), vec4fs(0)); } void free_glyph_render_sync(struct free_glyph_render fgr) { glBufferSubData(GL_ARRAY_BUFFER, 0, (ssize_t) (fgr.glyphs.size * sizeof(struct free_glyph)), fgr.glyphs.items); } struct free_glyph_render free_glyph_render_gen_buffer(struct free_glyph_render fgr) { glGenVertexArrays(1, &fgr.vao); glBindVertexArray(fgr.vao); glGenBuffers(1, &fgr.vbo); glBindBuffer(GL_ARRAY_BUFFER, fgr.vbo); glBufferData(GL_ARRAY_BUFFER, (ssize_t)(fgr.glyphs.cap * sizeof(struct free_glyph)), fgr.glyphs.items, GL_DYNAMIC_DRAW); glyph_attr_declare(free_glyph_get_attrs(), FREE_GLYPH_ATTR_TOTAL); return fgr; } struct free_glyph_render free_glyph_render_load_texture_atlas_or_exit(struct free_glyph_render fgr, FT_Face font_face) { struct vec2ui dim = free_glyph_render_calc_atlas_dimensions(font_face); fgr.atlas_dim = dim; glActiveTexture(GL_TEXTURE0); glGenTextures(1, &fgr.font_tex); glBindTexture(GL_TEXTURE_2D, fgr.font_tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, (int32_t)dim.x, (int32_t)dim.y, 0, GL_RED, GL_UNSIGNED_BYTE, 0); FT_GlyphSlot g = font_face->glyph; uint64_t x = 0; for ( uint64_t i = 0; i < 128; ++i ) { if ( FT_Load_Char(font_face, i, FT_LOAD_RENDER) ) { fprintf(stderr, "[ERROR] Failed to load char: %c\n", (char)i); exit(EXIT_FAILURE); } if ( FT_Render_Glyph(g, FT_RENDER_MODE_NORMAL) ) { fprintf(stderr, "[ERROR] Failed to render char: %c\n", (char)i); exit(EXIT_FAILURE); } glTexSubImage2D(GL_TEXTURE_2D, 0, (int32_t) x, 0, (int32_t) g->bitmap.width, (int32_t) g->bitmap.rows, GL_RED, GL_UNSIGNED_BYTE, g->bitmap.buffer); fgr.g_infos[i] = (struct glyph_info) { .a = { .x = g->advance.x >> 6, .y = g->advance.y >> 6, }, .b = { .w = g->bitmap.width, .h = g->bitmap.rows, .l = g->bitmap_left, .t = g->bitmap_top, }, .tx = ((float) x) / ((float) dim.x), }; x += g->bitmap.width; } return fgr; } struct vec2ui free_glyph_render_calc_atlas_dimensions(FT_Face font_face) { struct vec2ui dim = {0}; for ( uint64_t i = 0; i < 128; ++i ) { if ( FT_Load_Char(font_face, i, FT_LOAD_RENDER) ) { fprintf(stderr, "[WARNING] Failed to load char: %c\n", (char)i); continue; } dim.x += font_face->glyph->bitmap.width; uint32_t row = font_face->glyph->bitmap.rows; dim.y = ( dim.y < row ) * row + ( ! (dim.y < row) ) * dim.y; } return dim; } #endif /* defined(FREE_GLYPH_IMP) || defined(IMP) */ #endif