diff --git a/text_rendering/Makefile b/text_rendering/Makefile index 9df2ab1..530574e 100644 --- a/text_rendering/Makefile +++ b/text_rendering/Makefile @@ -1,7 +1,7 @@ -LDFLAGS = -lm -lgrapheme +LDFLAGS = -lm -lgrapheme -lSDL2 -lGLEW -lGL CFLAGS = -g -Wall -Wextra -pedantic -SOURCE = main.c font.c cache.c hash.c util.c -HEADER = font.h hash.h cache.h util.h +SOURCE = main.c font.c cache.c hash.c util.c ren.c +HEADER = font.h hash.h cache.h util.h ren.h test: ${HEADER} ${SOURCE} gcc ${CFLAGS} ${LDFLAGS} ${SOURCE} -o test diff --git a/text_rendering/atlas.png b/text_rendering/atlas.png index 754a74d..18aabd1 100644 Binary files a/text_rendering/atlas.png and b/text_rendering/atlas.png differ diff --git a/text_rendering/font.c b/text_rendering/font.c index c50ac54..036cfce 100644 --- a/text_rendering/font.c +++ b/text_rendering/font.c @@ -25,27 +25,22 @@ // this way the texture atlas for the font will be bigger but we save up the space // needed for rendering the font in multiple sizes - -struct font_atlas { - unsigned int glyphs, width, height; - unsigned char *atlas; - - struct { - stbtt_fontinfo stb; - msdf_AllocCtx ctx; - msdf_Result msdf; - float scale; - } priv; - - int file_size; - unsigned char *file; -}; +// as of now only monospaced fonts work correctly since no kerning information is stored const unsigned int glyph_w = 32; const unsigned int glyph_h = 32; +struct priv { + stbtt_fontinfo stb; + msdf_AllocCtx ctx; + msdf_Result msdf; + float scale; +}; +#define PRIV(x) ((struct priv *)x->priv) + + // only useful for msdf_c static inline void * _emalloc(size_t x, void *_) { (void)_; return emalloc(x); } static inline void _efree(void *x, void *_) { (void)_; efree(x); } @@ -53,7 +48,11 @@ static inline void _efree(void *x, void *_) { (void)_; efree(x); } struct font_atlas * font_init(void) { - return emalloc(sizeof(struct font_atlas)); + struct font_atlas *p = emalloc(sizeof(struct font_atlas)); + memset(p, 0, sizeof(struct font_atlas)); + p->priv = emalloc(sizeof(struct priv)); + memset(p->priv, 0, sizeof(struct priv)); + return p; } @@ -67,28 +66,35 @@ int font_load(struct font_atlas *atlas, const char *path) dump_file(path, &(atlas->file), &(atlas->file_size)); - err = stbtt_InitFont(&(atlas->priv.stb), atlas->file, 0); + err = stbtt_InitFont(&(PRIV(atlas)->stb), (unsigned char *)atlas->file, 0); ERROR(err == 0, -1); - atlas->priv.scale = stbtt_ScaleForPixelHeight(&(atlas->priv.stb), glyph_h); + PRIV(atlas)->scale = stbtt_ScaleForPixelHeight(&(PRIV(atlas)->stb), glyph_h); //int ascent, descent, linegap, baseline; //int x0,y0,x1,y1; - //stbtt_GetFontVMetrics(&(atlas->priv.stb), &ascent, &descent, &linegap); - //stbtt_GetFontBoundingBox(&(atlas->priv.stb), &x0, &y0, &x1, &y1); - //baseline = atlas->priv.scale * -y0; - //atlas->glyph_max_w = (atlas->priv.scale*x1) - (atlas->priv.scale*x0); - //atlas->glyph_max_h = (baseline+atlas->priv.scale*y1) - (baseline+atlas->priv.scale*y0); + //stbtt_GetFontVMetrics(&(PRIV(atlas)->stb), &ascent, &descent, &linegap); + //stbtt_GetFontBoundingBox(&(PRIV(atlas)->stb), &x0, &y0, &x1, &y1); + //baseline = PRIV(atlas)->scale * -y0; + //atlas->glyph_max_w = (PRIV(atlas)->scale*x1) - (PRIV(atlas)->scale*x0); + //atlas->glyph_max_h = (baseline+PRIV(atlas)->scale*y1) - (baseline+PRIV(atlas)->scale*y0); //atlas->atlas = emalloc(atlas->glyph_max_w*atlas->glyph_max_h*CACHE_SIZE); - atlas->atlas = ecalloc(CACHE_SIZE, BDEPTH*glyph_h*glyph_w); + atlas->atlas = emalloc(CACHE_SIZE*BDEPTH*glyph_h*glyph_w); memset(atlas->atlas, 0, CACHE_SIZE*BDEPTH*glyph_h*glyph_w); - atlas->width = glyph_w*CACHE_SIZE/2; - atlas->height = glyph_h*CACHE_SIZE/2; + // FIXME: make this a square atlas + atlas->width = glyph_w*CACHE_SIZE/4; + atlas->height = glyph_h*4; - atlas->priv.ctx = (msdf_AllocCtx){_emalloc, _efree, NULL}; + PRIV(atlas)->ctx = (msdf_AllocCtx){_emalloc, _efree, NULL}; cache_init(); + // preallocate all ascii characters + for (char c = ' '; c <= '~'; c++) { + if (!font_get_glyph_texture(atlas, c, NULL)) + return -1; + } + return 0; } @@ -97,6 +103,7 @@ int font_free(struct font_atlas *atlas) { efree(atlas->atlas); efree(atlas->file); + efree(atlas->priv); efree(atlas); cache_destroy(); return 0; @@ -105,33 +112,38 @@ int font_free(struct font_atlas *atlas) // FIXME: when generating the sdf I only use the height, so to not encounter memory // errors height and width must be equal -const struct font_glyph * font_get_glyph_texture(struct font_atlas *atlas, unsigned int code) +const struct font_glyph * font_get_glyph_texture(struct font_atlas *atlas, unsigned int code, int *updated) { + int u = 0; + if (!updated) updated = &u; + const struct font_glyph *r; - if ((r = cache_search(code)) != NULL) + if ((r = cache_search(code)) != NULL) { + *updated = 0; return r; + } + *updated = 1; // generate the sdf and put it into the cache // TODO: generate the whole block at once - int idx = stbtt_FindGlyphIndex(&atlas->priv.stb, code); + int idx = stbtt_FindGlyphIndex(&PRIV(atlas)->stb, code); // FIXME: what happens if I change the range? int err; - err = msdf_genGlyph(&atlas->priv.msdf, - &atlas->priv.stb, + err = msdf_genGlyph(&PRIV(atlas)->msdf, + &PRIV(atlas)->stb, idx, BORDER, - atlas->priv.scale, + PRIV(atlas)->scale, 2.0f/glyph_h, - &atlas->priv.ctx); + &PRIV(atlas)->ctx); // msdf_genGlyph returns 0 only when there are no contours, so only for // whitespace and such, for those insert a zero uv map into the cache // FIXME: this is a waste of space if (!err) { - atlas->priv.msdf.width = 0; - atlas->priv.msdf.height = 0; + PRIV(atlas)->msdf.width = 0; + PRIV(atlas)->msdf.height = 0; } - unsigned int spot = cache_get(); unsigned int oy = (glyph_h * spot) / atlas->width; unsigned int ox = (glyph_h * spot) % atlas->height; @@ -139,7 +151,7 @@ const struct font_glyph * font_get_glyph_texture(struct font_atlas *atlas, unsig // sum magic shit struct {unsigned char r,g,b;} *a = (void *)atlas->atlas; - msdf_Result *res = &atlas->priv.msdf; + msdf_Result *res = &PRIV(atlas)->msdf; float s = glyph_h; float tw = ((s * 0.7f) + s) / (s * 2.0f); float ta = tw - 0.5f; @@ -178,7 +190,11 @@ const struct font_glyph * font_get_glyph_texture(struct font_atlas *atlas, unsig .w = res->width, .h = res->height, }; - return cache_insert(&g, spot); + const struct font_glyph *ret = cache_insert(&g, spot); + + efree(PRIV(atlas)->msdf.rgb); + + return ret; } @@ -186,9 +202,8 @@ void font_dump(const struct font_atlas *atlas, const char *path) { stbi_write_png( path, - //atlas->width, - //atlas->height, - 128, 128, + atlas->width, + atlas->height, BDEPTH, atlas->atlas, BDEPTH*atlas->width); diff --git a/text_rendering/font.h b/text_rendering/font.h index 174dc01..b5d31d6 100644 --- a/text_rendering/font.h +++ b/text_rendering/font.h @@ -24,13 +24,19 @@ struct font_glyph { unsigned int u, v, w, h; }; +struct font_atlas { + unsigned int glyphs, width, height; + unsigned char *atlas; + int file_size; + char *file; + void *priv; +}; -struct font_atlas; struct font_atlas * font_init(void); int font_load(struct font_atlas *atlas, const char *path); int font_free(struct font_atlas *atlas); -const struct font_glyph * font_get_glyph_texture(struct font_atlas *atlas, unsigned int code); +const struct font_glyph * font_get_glyph_texture(struct font_atlas *atlas, unsigned int code, int *updated); void font_dump(const struct font_atlas *atlas, const char *path); #endif diff --git a/text_rendering/main.c b/text_rendering/main.c index 61ffcb4..9c6e7c5 100644 --- a/text_rendering/main.c +++ b/text_rendering/main.c @@ -1,33 +1,30 @@ #include #include -#include -#include "cache.h" -#include "font.h" +#include "ren.h" #include "util.h" int main(void) { - int err; - struct font_atlas *at = font_init(); - err = font_load(at, "./monospace.ttf"); - ERROR(err, -1, printf("failed to load font\n")); - - const char *s = "ciao mamma"; - const struct font_glyph *g; - size_t ret, off; - uint_least32_t cp; - for (off = 0; (ret = grapheme_decode_utf8(s+off, SIZE_MAX, &cp)) > 0 && cp != 0; off += ret) { - printf("%.*s (%d) -> %d\n", (int)ret, s+off, (int)ret, cp); - g = font_get_glyph_texture(at, cp); - if (!g) - printf("g is NULL\n"); + SDL_Window *win = SDL_CreateWindow( + "test render", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + 500, + 500, + SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); + if (ren_init(win)) { + printf("renderer init error: %s\n", ren_strerror()); + return 1; } - font_dump(at, "./atlas.png"); + ren_render_text("ciao mamma", 100, 100, 100, 100, 12); + SDL_GL_SwapWindow(win); - font_free(at); + while(1); + ren_free(); + SDL_DestroyWindow(win); return 0; } diff --git a/text_rendering/ren.c b/text_rendering/ren.c index 90313c4..36d0847 100644 --- a/text_rendering/ren.c +++ b/text_rendering/ren.c @@ -1,53 +1,423 @@ #include #include #include +#include +#include +#include +#include "util.h" +#include "font.h" +#include "ren.h" -#define GLSL_VERT_SHADER "vertex.glsl" -#define GLSL_FRAG_SHADER "fragment.glsl" -#define PACKED __attribute__((packed)) + +enum REN_ERR { + REN_SUCCESS = 0, + REN_ERRNO, + REN_INVAL, + REN_VERTEX, + REN_FRAGMENT, + REN_PROGRAM, + REN_COMPILE, + REN_LINK, + REN_TEXTURE, + REN_CONTEXT, + REN_GLEW, + REN_FONT, + REN_BUFFER, +}; + + +// TODO: make a macro for enum-associated string arrays +const char * ren_err_msg[] = { + [REN_SUCCESS] = "Success", + [REN_ERRNO] = "Look at errno", + [REN_INVAL] = "Invalid or NULL arguments", + [REN_VERTEX] = "Failed to create opengl vertex shader", + [REN_FRAGMENT] = "Failed to create opengl fragment shader", + [REN_PROGRAM] = "Failed to create opengl program", + [REN_COMPILE] = "Failed to compile shaders", + [REN_LINK] = "Failed to link shaders", + [REN_TEXTURE] = "Failed to create texture", + [REN_CONTEXT] = "Failed to create SDL OpenGL context", + [REN_GLEW] = "GLEW Error", + [REN_FONT] = "Font Error", + [REN_BUFFER] = "Failed to create opengl buffer", +}; + +// different stacks +#include "stack.h" +STACK_DECL(vtstack, struct v_text) +STACK_DECL(vcstack, struct v_col) struct { - SDL_Window *w; SDL_GLContext *gl; - GLuint gl_vertbuffer; - GLuint gl_program; + struct font_atlas *font; GLuint font_texture; + GLuint font_prog; + GLuint box_prog; + GLuint font_buffer; + struct vtstack font_stack; + struct vcstack box_stack; } ren = {0}; -typedef struct PACKED { - union { GLint x, u; }; - union { GLint y, v; }; -} vec2_i; +static int ren_errno; -typedef struct PACKED { - union { GLint x, r; }; - union { GLint y, g; }; - union { GLint z, b; }; - union { GLint w, a; }; -} vec4_i; -// textured vertex -struct PACKED v_text { - vec2_i pos; - vec2_i uv; -}; +// print shader compilation errors +static int shader_compile_error(GLuint shader) +{ + GLint status; + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if (status != GL_FALSE) + return 0; -// colored vertex -struct PACKED v_col { - vec2_i pos; - vec2_i col; -}; + GLint log_length; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length); + GLchar *log_str = emalloc((log_length + 1)*sizeof(GLchar)); + glGetShaderInfoLog(shader, log_length, NULL, log_str); -// different stacks -#define _STACK_NAME vtstack -#define _STACK_TYPE struct v_text -#include "stack.h" -#undef _STACK_NAME -#undef _STACK_TYPE -#define _STACK_NAME vcstack -#define _STACK_TYPE struct v_col -#include "stack.h" + const char *shader_type_str = NULL; + GLint shader_type; + glGetShaderiv(shader, GL_SHADER_TYPE, &shader_type); + switch(shader_type) { + case GL_VERTEX_SHADER: shader_type_str = "vertex"; break; + case GL_GEOMETRY_SHADER: shader_type_str = "geometry"; break; + case GL_FRAGMENT_SHADER: shader_type_str = "fragment"; break; + } + + fprintf(stderr, "Compile failure in %s shader:\n%s\n", shader_type_str, log_str); + efree(log_str); + return -1; +} + + +// print shader link errors +static int shader_link_error(GLuint prog) +{ + GLint status; + glGetProgramiv (prog, GL_LINK_STATUS, &status); + if (status != GL_FALSE) + return 0; + + GLint log_length; + glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &log_length); + + GLchar *log_str = emalloc((log_length + 1)*sizeof(GLchar)); + glGetProgramInfoLog(prog, log_length, NULL, log_str); + fprintf(stderr, "Linker failure: %s\n", log_str); + efree(log_str); + + return -1; +} + + +#define REN_RET(a,b) {ren_errno = b; return a;} +static GLuint shader_compile(const char *shader, GLuint type) +{ + GLuint s; + // initialize the vertex shader and get the corresponding id + s = glCreateShader(type); + if (!s) REN_RET(0, REN_VERTEX) + // get the shader into opengl + glShaderSource(s, 1, &shader, NULL); + glCompileShader(s); + if (shader_compile_error(s)) + REN_RET(0, REN_COMPILE) + return s; +} + + +const char * ren_strerror(void) +{ + return ren_err_msg[ren_errno % (sizeof(ren_err_msg)/sizeof(char *))]; +} + + +// compile a vertex shader (vs_path) and a fragment shader (fs_path) into an opengl +// program and return it's index +static GLuint ren_compile_program(const char *vs_path, const char *fs_path) +{ + GLuint gl_vertshader, gl_fragshader, prog; + + if (!vs_path || !fs_path) + REN_RET(0, REN_INVAL) + + char *str = NULL; + + dump_file(vs_path, &str, NULL); + if (!str) REN_RET(0, REN_ERRNO) + gl_vertshader = shader_compile(str, GL_VERTEX_SHADER); + efree(str); + if (!gl_vertshader) + return 0; + + dump_file(fs_path, &str, NULL); + if (!str) REN_RET(0, REN_ERRNO) + gl_fragshader = shader_compile(str, GL_FRAGMENT_SHADER); + efree(str); + if (!gl_fragshader) + return 0; + + + // create the main program object, it is an amalgamation of all shaders + prog = glCreateProgram(); + if (!prog) REN_RET(0, REN_PROGRAM) + + // attach the shaders to the program (set which shaders are present) + glAttachShader(prog, gl_vertshader); + glAttachShader(prog, gl_fragshader); + // then link the program (basically the linking stage of the program) + glLinkProgram(prog); + if (shader_link_error(prog)) + REN_RET(0, REN_LINK) + + // after linking the shaders can be detached and the source freed from + // memory since the program is ready to use + glDetachShader(prog, gl_vertshader); + glDetachShader(prog, gl_fragshader); + + return prog; +} + + +static GLuint ren_texturergb_2d(const char *buf, int w, int h, int upscale, int downscale) +{ + GLuint t; + + if (!buf || w <= 0 || h <= 0) + REN_RET(0, REN_INVAL) + + glGenTextures(1, &t); + if (!t) REN_RET(0, REN_TEXTURE) + + glBindTexture(GL_TEXTURE_2D, t); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, buf); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, downscale); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, upscale); + + return t; +} + + +static GLuint ren_texturergba_2d(const char *buf, int w, int h, int upscale, int downscale) +{ + GLuint t; + + if (!buf || w <= 0 || h <= 0) + REN_RET(0, REN_INVAL) + + glGenTextures(1, &t); + if (!t) REN_RET(0, REN_TEXTURE) + + glBindTexture(GL_TEXTURE_2D, t); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, downscale); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, upscale); + + return t; +} + + +// FIXME: update only the newly generated character instead of the whole texture +static int update_font_texture(void) +{ + glTexSubImage2D( + ren.font_texture, + 0, 0, 0, + ren.font->width, + ren.font->height, + GL_RGB, + GL_UNSIGNED_BYTE, + ren.font->atlas); + font_dump(ren.font, "./atlas.png"); + return 0; +} + + +// TODO: push window size uniforms +int ren_init(SDL_Window *w) +{ + if (!w) + REN_RET(-1, REN_INVAL) + ren.gl = SDL_GL_CreateContext(w); + if (!ren.gl) + REN_RET(-1, REN_CONTEXT) + + // select some features + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_SCISSOR_TEST); + glEnable(GL_TEXTURE_2D); + + GLenum glew_err = glewInit(); + if (glew_err != GLEW_OK) + REN_RET(glew_err, REN_GLEW); + + ren.font = font_init(); + if (!ren.font) REN_RET(-1, REN_FONT) + if (font_load(ren.font, FONT_PATH)) REN_RET(-1, REN_FONT) + + ren.font_texture = ren_texturergb_2d( + (const char *)ren.font->atlas, + ren.font->width, + ren.font->height, + GL_LINEAR, GL_LINEAR); + if (!ren.font_texture) return -1; + + ren.font_prog = ren_compile_program(FONT_VERSHADER, FONT_FRAGSHADER); + if (!ren.font_prog) return -1; + + ren.font_stack = vtstack_init(); + ren.box_stack = vcstack_init(); + + glGenBuffers(1, &ren.font_buffer); + if (!ren.font_buffer) REN_RET(-1, REN_BUFFER) + glBindBuffer(GL_ARRAY_BUFFER, ren.font_buffer); + glEnableVertexAttribArray(REN_VERTEX_IDX); + glVertexAttribPointer( + REN_VERTEX_IDX, + 2, + GL_INT, + GL_FALSE, + sizeof(struct v_text), + 0); + glEnableVertexAttribArray(REN_UV_IDX); + glVertexAttribPointer( + REN_UV_IDX, + 2, + GL_INT, + GL_FALSE, + sizeof(struct v_text), + (void*)sizeof(vec2_i)); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + int width, height; + SDL_GetWindowSize(w, &width, &height); + ren_update_viewport(width, height); + glClearColor(0.3f, 0.3f, 0.3f, 0.f); + glClear(GL_COLOR_BUFFER_BIT); + + return 0; +} + + +static int ren_draw_font_stack(void) +{ + glUseProgram(ren.font_prog); + glBindBuffer(GL_ARRAY_BUFFER, ren.font_buffer); + + // TODO: implement size and damage tracking on stacks + glBufferData( + GL_ARRAY_BUFFER, + ren.font_stack.idx*sizeof(struct v_text), + ren.font_stack.items, + GL_DYNAMIC_DRAW); + glDrawArrays(GL_TRIANGLES, 0, ren.font_stack.idx); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glUseProgram(0); + return 0; +} + + +int ren_update_viewport(int w, int h) +{ + glViewport(0, 0, w, h); + glUniform2i(REN_SCREENSIZE_LOC, w, h); + return 0; +} + + +// TODO: implement font size +int ren_render_text(const char *str, int x, int y, int w, int h, int size) +{ + // x4,y4 x3,y3 + // +-------------+ + // |(x,y) /| + // | / | + // | 2 / | + // | / | + // | / | + // | / 1 | + // |/ | + // +-------------+ + // x1,y1 x2,y2 + + const struct font_glyph *g; + size_t ret, off; + uint_least32_t cp; + int updated, gx = x, gy = y; + struct v_text v; + for (off = 0; (ret = grapheme_decode_utf8(str+off, SIZE_MAX, &cp)) > 0 && cp != 0; off += ret) { + g = font_get_glyph_texture(ren.font, cp, &updated); + if (!g) REN_RET(-1, REN_FONT); + if (updated) { + if (update_font_texture()) + REN_RET(-1, REN_TEXTURE); + } + + // x1,y1 + v = (struct v_text){ + .pos = { .x = gx, .y = gy+g->h }, + .uv = { .u = g->u, .v = g->v+g->h }, + }; + vtstack_push(&ren.font_stack, &v); + // x2,y2 + v = (struct v_text){ + .pos = { .x = gx+g->w, .y = gy+g->h }, + .uv = { .u = g->u+g->w, .v = g->v+g->h }, + }; + vtstack_push(&ren.font_stack, &v); + // x3,y3 + v = (struct v_text){ + .pos = { .x = gx+g->w, .y = gy }, + .uv = { .u = g->u+g->w, .v = g->v }, + }; + vtstack_push(&ren.font_stack, &v); + // x4,y4 + v = (struct v_text){ + .pos = { .x = gx, .y = gy }, + .uv = { .u = g->u, .v = g->v }, + }; + vtstack_push(&ren.font_stack, &v); + + // TODO: possible kerning needs to be applied here + gx += g->w; + if (cp == '\n') + gy += 20; // FIXME: encode and/or store line height + } + + // FIXME: here we are doing one draw call for string of text which is + // inefficient but is simpler and allows for individual scissors + glScissor(x, y, w, h); + ren_draw_font_stack(); + vtstack_clear(&ren.font_stack); + + return 0; +} + + +int ren_free(void) +{ + glUseProgram(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glDeleteTextures(1, &ren.font_texture); + glDeleteBuffers(1, &ren.font_buffer); + 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 new file mode 100644 index 0000000..98d9e00 --- /dev/null +++ b/text_rendering/ren.h @@ -0,0 +1,48 @@ +#ifndef _RENDERER_H +#define _RENDERER_H + +#include + + +#define FONT_PATH "./monospace.ttf" +#define FONT_VERSHADER "./font_vertshader.glsl" +#define FONT_FRAGSHADER "./font_fragshader.glsl" +#define REN_VERTEX_IDX 0 +#define REN_UV_IDX 1 +#define REN_COLOR_IDX 2 +#define REN_SCREENSIZE_LOC 0 + + +typedef struct { + union { int x, u; }; + union { int y, v; }; +} vec2_i; + +typedef struct { + union { int x, r; }; + union { int y, g; }; + union { int z, b; }; + union { int w, a; }; +} vec4_i; + +// textured vertex +struct v_text { + vec2_i pos; + vec2_i uv; +}; + +// colored vertex +struct v_col { + vec2_i pos; + vec2_i col; +}; + + +int ren_init(SDL_Window *sdl_window); +int ren_free(void); +const char * ren_strerror(void); +int ren_update_viewport(int w, int h); +int ren_render_text(const char *str, int x, int y, int w, int h, int size); + + +#endif diff --git a/text_rendering/stack.h b/text_rendering/stack.h index 4074b1f..63cfd25 100644 --- a/text_rendering/stack.h +++ b/text_rendering/stack.h @@ -1,61 +1,72 @@ -#ifdef _STACK_TYPE -#ifdef _STACK_NAME +#ifndef _STACK_GENERIC_H +#define _STACK_GENERIC_H -#ifndef _STACK_REALLOC - #define _STACK_REALLOC(p, s) realloc(p,s) -#endif -#ifndef _STACK_MEMSET - #define _STACK_MEMSET(p, c, s) memset(p,c,s) -#endif -#ifndef _STACK_STEP - #define _STACK_STEP 8 -#endif -#ifndef _STACK_CAT - #define _STACK_CAT_I(a,b) a ## b - #define _STACK_CAT(a,b) _STACK_CAT_I(a,b) -#endif +#define STACK_STEP 8 // TODO: add a rolling hash -struct _STACK_NAME { - _STACK_TYPE *items; - int size, idx; -}; - - -int _STACK_CAT(stack_grow_,_STACK_NAME)(struct _STACK_NAME *stack, int step) -{ - if (!stack) - return -1; - stack->items = _STACK_REALLOC(stack->items, (stack->size+step)*sizeof(_STACK_TYPE)); - if(!stack->items) - return -1; - _STACK_MEMSET(&(stack->items[stack->size]), 0, step*sizeof(*(stack->items))); - stack->size += step; - return 0; -} +#define STACK_DECL(stackname, type) \ +struct stackname { \ + type *items; \ + int size, idx; \ +}; \ +\ +\ +struct stackname stackname##_init(void) { return (struct stackname){0}; } \ +\ +\ +int stackname##_grow(struct stackname *stack, int step) \ +{ \ + if (!stack) \ + return -1; \ + stack->items = realloc(stack->items, (stack->size+step)*sizeof(type)); \ + if(!stack->items) \ + return -1; \ + memset(&(stack->items[stack->size]), 0, step*sizeof(*(stack->items))); \ + stack->size += step; \ + return 0; \ +} \ +\ +\ +int stackname##_push(struct stackname *stack, type *e) \ +{ \ + if (!stack || !e) \ + return -1; \ + if (stack->idx >= stack->size) \ + if (stackname##_grow(stack, STACK_STEP)) \ + return -1; \ + stack->items[stack->idx++] = *e; \ + return 0; \ +} \ +\ +\ +type stackname##_pop(struct stackname *stack) \ +{ \ + if (!stack || stack->idx == 0 || stack->size == 0) \ + return (type){0}; \ + return stack->items[stack->idx--]; \ +} \ +\ +\ +int stackname##_clear(struct stackname *stack) \ +{ \ + if (!stack) \ + return -1; \ + stack->idx = 0; \ + return 0; \ +} \ +\ +\ +int stackname##_free(struct stackname *stack) \ +{ \ + if (stack) { \ + stackname##_clear(stack); \ + if (stack->items) \ + free(stack->items); \ + } \ + return 0; \ +} \ -int _STACK_CAT(stack_push_,_STACK_NAME)(struct _STACK_NAME *stack, _STACK_TYPE *e) -{ - if (!stack || !e) - return -1; - if (stack->idx >= stack->size) - if (_STACK_CAT(stack_grow_,_STACK_NAME)(stack, _STACK_STEP)) - return -1; - stack->items[stack->idx++] = *e; - return 0; -} - - -int _STACK_CAT(stack_clear_,_STACK_NAME)(struct _STACK_NAME *stack) -{ - if (!stack) - return -1; - stack->idx = 0; -} - - -#endif #endif diff --git a/text_rendering/test b/text_rendering/test index 69d543b..5efa58c 100755 Binary files a/text_rendering/test and b/text_rendering/test differ diff --git a/text_rendering/util.c b/text_rendering/util.c index 66ab3a1..626f1bf 100644 --- a/text_rendering/util.c +++ b/text_rendering/util.c @@ -65,7 +65,7 @@ void map_file(const unsigned char **str, int *size, const char *path) } -void dump_file(const char *path, unsigned char **buf, int *buf_len) +void dump_file(const char *path, char **buf, int *buf_len) { if (!path) { errno = EINVAL; @@ -75,10 +75,9 @@ void dump_file(const char *path, unsigned char **buf, int *buf_len) errno = EINVAL; err(EXIT_FAILURE, "No buffer specified"); } - if (!buf_len) { - errno = EINVAL; - err(EXIT_FAILURE, "Nowhere to store buffer size"); - } + int m = 0; + if (!buf_len) + buf_len = &m; FILE *fp = fopen(path, "r"); if (!fp) err(EXIT_FAILURE, "Cannot open file %s", path); diff --git a/text_rendering/util.h b/text_rendering/util.h index b8d0161..23098b5 100644 --- a/text_rendering/util.h +++ b/text_rendering/util.h @@ -10,7 +10,7 @@ void * ecalloc(unsigned long int nmemb, unsigned long int size); void efree(void *ptr); void map_file(const unsigned char **str, int *size, const char *path); -void dump_file(const char *path, unsigned char **buf, int *buf_len); +void dump_file(const char *path, char **buf, int *buf_len); void print_byte(unsigned char byte);