load fonts at runtime

master
Alessandro Mauri 2 years ago
parent 30510ab69a
commit 93f93a594e
  1. 4
      text_rendering/main.c
  2. 108
      text_rendering/ren.c
  3. 2
      text_rendering/ren.h
  4. 8
      text_rendering/util.c
  5. 1
      text_rendering/util.h

@ -15,7 +15,9 @@ SDL_Window *win;
void draw(void) void draw(void)
{ {
ren_clear(); 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); ren_render_box(100, 300, 50, 50, 0xffff0000);
SDL_GL_SwapWindow(win); SDL_GL_SwapWindow(win);
} }

@ -74,10 +74,15 @@ STACK_DECL(vtstack, struct v_text)
STACK_DECL(vcstack, struct v_col) STACK_DECL(vcstack, struct v_col)
struct ren_font {
struct font_atlas *font;
GLuint texture;
};
struct { struct {
SDL_GLContext *gl; SDL_GLContext *gl;
struct font_atlas *font; struct ren_font *fonts;
GLuint font_texture; int fonts_no;
GLuint font_prog; GLuint font_prog;
GLuint box_prog; GLuint box_prog;
GLuint font_buffer; 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 // 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(glTexSubImage2D(
GL_TEXTURE_RECTANGLE, GL_TEXTURE_RECTANGLE,
0, 0, 0, 0, 0, 0,
ren.font->width, ren.fonts[idx].font->width,
ren.font->height, ren.fonts[idx].font->height,
GL_RED, GL_RED,
GL_UNSIGNED_BYTE, GL_UNSIGNED_BYTE,
ren.font->atlas)) ren.fonts[idx].font->atlas))
font_dump(ren.font, "./atlas.png"); font_dump(ren.fonts[idx].font, "./atlas.png");
glUseProgram(0); GL(glUseProgram(0));
return 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 // TODO: push window size uniforms
int ren_init(SDL_Window *w) int ren_init(SDL_Window *w)
{ {
@ -343,19 +393,6 @@ int ren_init(SDL_Window *w)
if (glew_err != GLEW_OK) if (glew_err != GLEW_OK)
REN_RET(glew_err, REN_GLEW); 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 // Create stacks
ren.font_stack = vtstack_init(); ren.font_stack = vtstack_init();
ren.box_stack = vcstack_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(glUseProgram(ren.font_prog))
GL(glBindBuffer(GL_ARRAY_BUFFER, ren.font_buffer)) 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 // 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(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.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_VERTEX_IDX))
GL(glEnableVertexAttribArray(REN_UV_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 // TODO: implement font size
int ren_render_text(const char *str, int x, int y, int w, int h, int 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 // FIXME: the bounding box (scissor) logic does not work
// TODO: create a method for calculating the total bounding box of a string // TODO: create a method for calculating the total bounding box of a string
// given the box size // 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; size_t ret, off;
uint_least32_t cp; uint_least32_t cp;
int updated, gx = x, gy = y; 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) { 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) // skip special characters that render a box (not present in font)
// FIXME: handle tab // FIXME: handle tab
if (iscntrl(cp)) goto skip_render; 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 (!g) REN_RET(-1, REN_FONT);
if (updated) { if (updated) {
if (update_font_texture()) if (update_font_texture(idx))
REN_RET(-1, REN_TEXTURE); 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; break;
case '\n': case '\n':
// TODO: encode and/or store line height // TODO: encode and/or store line height
gy += ren.font->glyph_max_h; gy += ren.fonts[idx].font->glyph_max_h;
gx = x; gx = x;
break; break;
default: 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 // FIXME: here we are doing one draw call for string of text which is
// inefficient but is simpler and allows for individual scissors // inefficient but is simpler and allows for individual scissors
ren_set_scissor(x, y, w, h); ren_set_scissor(x, y, w, h);
ren_draw_font_stack(); ren_draw_font_stack(idx);
return 0; return 0;
} }
@ -710,11 +748,13 @@ int ren_free(void)
GL(glBindBuffer(GL_ARRAY_BUFFER, 0)) GL(glBindBuffer(GL_ARRAY_BUFFER, 0))
GL(glDeleteProgram(ren.box_prog)); GL(glDeleteProgram(ren.box_prog));
GL(glDeleteProgram(ren.font_prog)); GL(glDeleteProgram(ren.font_prog));
GL(glDeleteTextures(1, &ren.font_texture))
GL(glDeleteBuffers(1, &ren.font_buffer)) 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); SDL_GL_DeleteContext(ren.gl);
vtstack_free(&ren.font_stack); vtstack_free(&ren.font_stack);
vcstack_free(&ren.box_stack); vcstack_free(&ren.box_stack);
font_free(ren.font);
return 0; return 0;
} }

@ -4,7 +4,7 @@
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#define FONT_PATH "./monospace.ttf" #define DEFAULT_FONT "./monospace.ttf"
#define FONT_VERSHADER "./font_vertshader.glsl" #define FONT_VERSHADER "./font_vertshader.glsl"
#define FONT_FRAGSHADER "./font_fragshader.glsl" #define FONT_FRAGSHADER "./font_fragshader.glsl"
#define BOX_VERSHADER "./box_vertshader.glsl" #define BOX_VERSHADER "./box_vertshader.glsl"

@ -38,6 +38,14 @@ void * ecalloc(unsigned long int nmemb, unsigned long int size)
return r; 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) void efree(void *ptr)
{ {

@ -7,6 +7,7 @@
void * emalloc(unsigned long int size); void * emalloc(unsigned long int size);
void * ecalloc(unsigned long int nmemb, 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 efree(void *ptr);
void map_file(const unsigned char **str, int *size, const char *path); void map_file(const unsigned char **str, int *size, const char *path);

Loading…
Cancel
Save