#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" #include "simple_render.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_atlas { uint32_t font_tex; struct vec2ui atlas_dim; struct glyph_info g_infos[128]; }; struct free_glyph_atlas fga_create(FT_Face font_face); float fga_calc_col(const struct free_glyph_atlas *fga, const struct app *app, const char *cstr, size_t cstr_size, struct vec2f pos, size_t index); float fga_calc_col_buffer(const struct free_glyph_atlas *fga, const struct app *app); void fga_calc(struct free_glyph_atlas *fga, struct simple_render *sr, const struct app *app, const char *cstr, size_t cstr_size, struct vec2f pos, struct vec4f fg_color, struct vec4f bg_color); void fga_calc_buffer(struct free_glyph_atlas *fga, struct simple_render *sr, const struct app *app, struct vec2f pos); void fga_load_texture_atlas_or_exit(struct free_glyph_atlas *fga, FT_Face font_face); struct vec2ui fga_calc_atlas_dimensions(FT_Face font_face); #if defined(FREE_GLYPH_IMP) || defined(IMP) #include "shader.h" #include "gl_util.h" struct free_glyph_atlas fga_create(FT_Face font_face) { struct free_glyph_atlas fga = {0}; fga_load_texture_atlas_or_exit(&fga, font_face); return fga; } float fga_calc_col(const struct free_glyph_atlas *fga, const 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(fga->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 = fga->g_infos[(int64_t)c]; if ( c == '\t' ) { c = ' '; gi = fga->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 fga_calc_col_buffer(const struct free_glyph_atlas *fga, const struct app *app) { return fga_calc_col(fga, app, app->buf.data.items, app->buf.data.size, vec2fs(0), app->buf.cur); } /* struct free_glyph_atlas */ /* fga_calc(struct free_glyph_atlas fga, 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(fga->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 = fga->g_infos[(int64_t)c]; */ /* if ( c == '\t' ) { */ /* c = ' '; */ /* gi = fga->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(fga->glyphs, gly); */ /* } */ /* return fga; */ /* } */ /* struct free_glyph_atlas */ /* fga_calc_buffer(struct free_glyph_atlas fga, */ /* struct app app, */ /* struct vec2f size) */ /* { */ /* return fga_calc(fga, app, */ /* app.buf.data.items, app.buf.data.size, */ /* size, vec4fs(1), vec4fs(0)); */ /* } */ void fga_load_texture_atlas_or_exit(struct free_glyph_atlas *fga, FT_Face font_face) { struct vec2ui dim = fga_calc_atlas_dimensions(font_face); fga->atlas_dim = dim; glActiveTexture(GL_TEXTURE0); glGenTextures(1, &fga->font_tex); glBindTexture(GL_TEXTURE_2D, fga->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); fga->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; } } struct vec2ui fga_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