From 93f93a594e51654e7c4fbdb0f65990f24bd6bae7 Mon Sep 17 00:00:00 2001 From: Alessandro Mauri Date: Sat, 4 Mar 2023 18:54:00 +0100 Subject: [PATCH] load fonts at runtime --- text_rendering/main.c | 4 +- text_rendering/ren.c | 108 +++++++++++++++++++++++++++++------------- text_rendering/ren.h | 2 +- text_rendering/util.c | 8 ++++ text_rendering/util.h | 1 + 5 files changed, 87 insertions(+), 36 deletions(-) diff --git a/text_rendering/main.c b/text_rendering/main.c index fc37bfe..27a7045 100644 --- a/text_rendering/main.c +++ b/text_rendering/main.c @@ -15,7 +15,9 @@ SDL_Window *win; void draw(void) { ren_clear(); - ren_render_text(str, 0, 0, 100, 50, 12); + if (ren_render_text(str, 0, 0, 100, 50, 20)) + printf("text: %s\n", ren_strerror()); + ren_render_text("altro font", 200, 40, 300, 300, 40); ren_render_box(100, 300, 50, 50, 0xffff0000); SDL_GL_SwapWindow(win); } diff --git a/text_rendering/ren.c b/text_rendering/ren.c index 1c3134d..19b2eed 100644 --- a/text_rendering/ren.c +++ b/text_rendering/ren.c @@ -74,10 +74,15 @@ STACK_DECL(vtstack, struct v_text) STACK_DECL(vcstack, struct v_col) +struct ren_font { + struct font_atlas *font; + GLuint texture; +}; + struct { SDL_GLContext *gl; - struct font_atlas *font; - GLuint font_texture; + struct ren_font *fonts; + int fonts_no; GLuint font_prog; GLuint box_prog; GLuint font_buffer; @@ -304,23 +309,68 @@ static GLuint ren_texturer_rect(const char *buf, int w, int h, int upscale, int // FIXME: update only the newly generated character instead of the whole texture -static int update_font_texture(void) +static int update_font_texture(int idx) { - glUseProgram(ren.font_prog); + GL(glUseProgram(ren.font_prog)) GL(glTexSubImage2D( GL_TEXTURE_RECTANGLE, 0, 0, 0, - ren.font->width, - ren.font->height, + ren.fonts[idx].font->width, + ren.fonts[idx].font->height, GL_RED, GL_UNSIGNED_BYTE, - ren.font->atlas)) - font_dump(ren.font, "./atlas.png"); - glUseProgram(0); + ren.fonts[idx].font->atlas)) + font_dump(ren.fonts[idx].font, "./atlas.png"); + GL(glUseProgram(0)); return 0; } +// loads a font and returns it's index in the fonts array +static int ren_load_font(int size, const char *path) +{ + int idx = ren.fonts_no; + struct font_atlas *f; + ren.fonts = erealloc(ren.fonts, sizeof(struct ren_font)*(idx+1)); + ren.fonts[idx].font = font_init(); + f = ren.fonts[idx].font; + if (!f) + REN_RET(-1, REN_FONT) + + if (!path) + path = DEFAULT_FONT; + + if (font_load(f, path, size)) + REN_RET(-1, REN_FONT) + font_dump(f, "./atlas.png"); + + // load font texture (atlas) + ren.fonts[idx].texture = ren_texturer_rect( + (const char *)f->atlas, + f->width, + f->height, + GL_LINEAR, GL_LINEAR); + if (!ren.fonts[idx].texture) + return -1; + + ren.fonts_no = idx+1; + return idx; +} + + +// returns the index to the ren.fonts array to the font with the correct size +// return -1 on errror +static int ren_get_font(int size) +{ + for (int i = 0; i < ren.fonts_no; i++) { + if (ren.fonts[i].font->size == size) + return i; + } + // TODO: add a way to change font family + return ren_load_font(size, NULL); +} + + // TODO: push window size uniforms int ren_init(SDL_Window *w) { @@ -343,19 +393,6 @@ int ren_init(SDL_Window *w) if (glew_err != GLEW_OK) REN_RET(glew_err, REN_GLEW); - // Load font - ren.font = font_init(); - if (!ren.font) REN_RET(-1, REN_FONT) - if (font_load(ren.font, FONT_PATH, 20)) REN_RET(-1, REN_FONT) - font_dump(ren.font, "./atlas.png"); - // load font texture (atlas) - ren.font_texture = ren_texturer_rect( - (const char *)ren.font->atlas, - ren.font->width, - ren.font->height, - GL_LINEAR, GL_LINEAR); - if (!ren.font_texture) return -1; - // Create stacks ren.font_stack = vtstack_init(); ren.box_stack = vcstack_init(); @@ -403,7 +440,9 @@ int ren_clear(void) } -static int ren_draw_font_stack(void) +// idx refers to the fonts array index, this means that when drawing the font stack +// only one font size at the time can be used +static int ren_draw_font_stack(int idx) { GL(glUseProgram(ren.font_prog)) GL(glBindBuffer(GL_ARRAY_BUFFER, ren.font_buffer)) @@ -412,7 +451,7 @@ static int ren_draw_font_stack(void) // this has caused me some trouble, convert from image coordiates to viewport GL(glScissor(ren.s_x, ren.height-ren.s_y-ren.s_h, ren.s_w, ren.s_h)) GL(glUniform2i(ren.viewsize_loc, ren.width, ren.height)) - GL(glUniform2i(ren.texturesize_loc, ren.font->width, ren.font->height)) + GL(glUniform2i(ren.texturesize_loc, ren.fonts[idx].font->width, ren.fonts[idx].font->height)) GL(glEnableVertexAttribArray(REN_VERTEX_IDX)) GL(glEnableVertexAttribArray(REN_UV_IDX)) @@ -595,10 +634,6 @@ static int ren_push_glyph(const struct font_glyph *g, int gx, int gy) // TODO: implement font size int ren_render_text(const char *str, int x, int y, int w, int h, int size) { - // TODO: check size, if the current font is not of the same size then load - // load a new font and use that texture instead, this implies making - // a system to store and use different fonts, like: - // struct font_atlas * font_by_size(int size); // FIXME: the bounding box (scissor) logic does not work // TODO: create a method for calculating the total bounding box of a string // given the box size @@ -606,14 +641,17 @@ int ren_render_text(const char *str, int x, int y, int w, int h, int size) size_t ret, off; uint_least32_t cp; int updated, gx = x, gy = y; + int idx = ren_get_font(size); + if (idx < 0) + return -1; for (off = 0; (ret = grapheme_decode_utf8(str+off, SIZE_MAX, &cp)) > 0 && cp != 0; off += ret) { // skip special characters that render a box (not present in font) // FIXME: handle tab if (iscntrl(cp)) goto skip_render; - g = font_get_glyph_texture(ren.font, cp, &updated); + g = font_get_glyph_texture(ren.fonts[idx].font, cp, &updated); if (!g) REN_RET(-1, REN_FONT); if (updated) { - if (update_font_texture()) + if (update_font_texture(idx)) REN_RET(-1, REN_TEXTURE); } @@ -631,7 +669,7 @@ int ren_render_text(const char *str, int x, int y, int w, int h, int size) break; case '\n': // TODO: encode and/or store line height - gy += ren.font->glyph_max_h; + gy += ren.fonts[idx].font->glyph_max_h; gx = x; break; default: break; @@ -641,7 +679,7 @@ int ren_render_text(const char *str, int x, int y, int w, int h, int size) // FIXME: here we are doing one draw call for string of text which is // inefficient but is simpler and allows for individual scissors ren_set_scissor(x, y, w, h); - ren_draw_font_stack(); + ren_draw_font_stack(idx); return 0; } @@ -710,11 +748,13 @@ int ren_free(void) GL(glBindBuffer(GL_ARRAY_BUFFER, 0)) GL(glDeleteProgram(ren.box_prog)); GL(glDeleteProgram(ren.font_prog)); - GL(glDeleteTextures(1, &ren.font_texture)) GL(glDeleteBuffers(1, &ren.font_buffer)) + for (int i = 0; i < ren.fonts_no; i++) { + GL(glDeleteTextures(1, &ren.fonts[i].texture)) + font_free(ren.fonts[i].font); + } SDL_GL_DeleteContext(ren.gl); vtstack_free(&ren.font_stack); vcstack_free(&ren.box_stack); - font_free(ren.font); return 0; } diff --git a/text_rendering/ren.h b/text_rendering/ren.h index 66a8b60..443dec1 100644 --- a/text_rendering/ren.h +++ b/text_rendering/ren.h @@ -4,7 +4,7 @@ #include -#define FONT_PATH "./monospace.ttf" +#define DEFAULT_FONT "./monospace.ttf" #define FONT_VERSHADER "./font_vertshader.glsl" #define FONT_FRAGSHADER "./font_fragshader.glsl" #define BOX_VERSHADER "./box_vertshader.glsl" diff --git a/text_rendering/util.c b/text_rendering/util.c index b6a8894..2d68db8 100644 --- a/text_rendering/util.c +++ b/text_rendering/util.c @@ -38,6 +38,14 @@ void * ecalloc(unsigned long int nmemb, unsigned long int size) return r; } +void * erealloc(void *ptr, unsigned long int size) +{ + void *r = realloc(ptr, size); + if (!r) + err(EXIT_FAILURE, "ralloc() of 0x%lx, to size %ld", (unsigned long)ptr, size); + return r; +} + void efree(void *ptr) { diff --git a/text_rendering/util.h b/text_rendering/util.h index 23098b5..fe254ea 100644 --- a/text_rendering/util.h +++ b/text_rendering/util.h @@ -7,6 +7,7 @@ void * emalloc(unsigned long int size); void * ecalloc(unsigned long int nmemb, unsigned long int size); +void * erealloc(void *ptr, unsigned long int size); void efree(void *ptr); void map_file(const unsigned char **str, int *size, const char *path);