I can't even

master
Alessandro Mauri 2 years ago
parent 805f633af0
commit dcce0ed11b
Signed by: alema
GPG Key ID: 2B7BF9531FF03BE8
  1. 6
      text_rendering/Makefile
  2. BIN
      text_rendering/atlas.png
  3. 93
      text_rendering/font.c
  4. 10
      text_rendering/font.h
  5. 35
      text_rendering/main.c
  6. 440
      text_rendering/ren.c
  7. 48
      text_rendering/ren.h
  8. 117
      text_rendering/stack.h
  9. BIN
      text_rendering/test
  10. 9
      text_rendering/util.c
  11. 2
      text_rendering/util.h

@ -1,7 +1,7 @@
LDFLAGS = -lm -lgrapheme LDFLAGS = -lm -lgrapheme -lSDL2 -lGLEW -lGL
CFLAGS = -g -Wall -Wextra -pedantic CFLAGS = -g -Wall -Wextra -pedantic
SOURCE = main.c font.c cache.c hash.c util.c SOURCE = main.c font.c cache.c hash.c util.c ren.c
HEADER = font.h hash.h cache.h util.h HEADER = font.h hash.h cache.h util.h ren.h
test: ${HEADER} ${SOURCE} test: ${HEADER} ${SOURCE}
gcc ${CFLAGS} ${LDFLAGS} ${SOURCE} -o test gcc ${CFLAGS} ${LDFLAGS} ${SOURCE} -o test

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

@ -25,25 +25,20 @@
// this way the texture atlas for the font will be bigger but we save up the space // 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 // needed for rendering the font in multiple sizes
// as of now only monospaced fonts work correctly since no kerning information is stored
struct font_atlas {
unsigned int glyphs, width, height;
unsigned char *atlas;
struct { const unsigned int glyph_w = 32;
const unsigned int glyph_h = 32;
struct priv {
stbtt_fontinfo stb; stbtt_fontinfo stb;
msdf_AllocCtx ctx; msdf_AllocCtx ctx;
msdf_Result msdf; msdf_Result msdf;
float scale; float scale;
} priv;
int file_size;
unsigned char *file;
}; };
#define PRIV(x) ((struct priv *)x->priv)
const unsigned int glyph_w = 32;
const unsigned int glyph_h = 32;
// only useful for msdf_c // only useful for msdf_c
@ -53,7 +48,11 @@ static inline void _efree(void *x, void *_) { (void)_; efree(x); }
struct font_atlas * font_init(void) 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)); 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); 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 ascent, descent, linegap, baseline;
//int x0,y0,x1,y1; //int x0,y0,x1,y1;
//stbtt_GetFontVMetrics(&(atlas->priv.stb), &ascent, &descent, &linegap); //stbtt_GetFontVMetrics(&(PRIV(atlas)->stb), &ascent, &descent, &linegap);
//stbtt_GetFontBoundingBox(&(atlas->priv.stb), &x0, &y0, &x1, &y1); //stbtt_GetFontBoundingBox(&(PRIV(atlas)->stb), &x0, &y0, &x1, &y1);
//baseline = atlas->priv.scale * -y0; //baseline = PRIV(atlas)->scale * -y0;
//atlas->glyph_max_w = (atlas->priv.scale*x1) - (atlas->priv.scale*x0); //atlas->glyph_max_w = (PRIV(atlas)->scale*x1) - (PRIV(atlas)->scale*x0);
//atlas->glyph_max_h = (baseline+atlas->priv.scale*y1) - (baseline+atlas->priv.scale*y0); //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 = 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); memset(atlas->atlas, 0, CACHE_SIZE*BDEPTH*glyph_h*glyph_w);
atlas->width = glyph_w*CACHE_SIZE/2; // FIXME: make this a square atlas
atlas->height = glyph_h*CACHE_SIZE/2; 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(); cache_init();
// preallocate all ascii characters
for (char c = ' '; c <= '~'; c++) {
if (!font_get_glyph_texture(atlas, c, NULL))
return -1;
}
return 0; return 0;
} }
@ -97,6 +103,7 @@ int font_free(struct font_atlas *atlas)
{ {
efree(atlas->atlas); efree(atlas->atlas);
efree(atlas->file); efree(atlas->file);
efree(atlas->priv);
efree(atlas); efree(atlas);
cache_destroy(); cache_destroy();
return 0; 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 // FIXME: when generating the sdf I only use the height, so to not encounter memory
// errors height and width must be equal // 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; const struct font_glyph *r;
if ((r = cache_search(code)) != NULL) if ((r = cache_search(code)) != NULL) {
*updated = 0;
return r; return r;
}
*updated = 1;
// generate the sdf and put it into the cache // generate the sdf and put it into the cache
// TODO: generate the whole block at once // 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? // FIXME: what happens if I change the range?
int err; int err;
err = msdf_genGlyph(&atlas->priv.msdf, err = msdf_genGlyph(&PRIV(atlas)->msdf,
&atlas->priv.stb, &PRIV(atlas)->stb,
idx, idx,
BORDER, BORDER,
atlas->priv.scale, PRIV(atlas)->scale,
2.0f/glyph_h, 2.0f/glyph_h,
&atlas->priv.ctx); &PRIV(atlas)->ctx);
// msdf_genGlyph returns 0 only when there are no contours, so only for // 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 // whitespace and such, for those insert a zero uv map into the cache
// FIXME: this is a waste of space // FIXME: this is a waste of space
if (!err) { if (!err) {
atlas->priv.msdf.width = 0; PRIV(atlas)->msdf.width = 0;
atlas->priv.msdf.height = 0; PRIV(atlas)->msdf.height = 0;
} }
unsigned int spot = cache_get(); unsigned int spot = cache_get();
unsigned int oy = (glyph_h * spot) / atlas->width; unsigned int oy = (glyph_h * spot) / atlas->width;
unsigned int ox = (glyph_h * spot) % atlas->height; 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 // sum magic shit
struct {unsigned char r,g,b;} *a = (void *)atlas->atlas; 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 s = glyph_h;
float tw = ((s * 0.7f) + s) / (s * 2.0f); float tw = ((s * 0.7f) + s) / (s * 2.0f);
float ta = tw - 0.5f; 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, .w = res->width,
.h = res->height, .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( stbi_write_png(
path, path,
//atlas->width, atlas->width,
//atlas->height, atlas->height,
128, 128,
BDEPTH, BDEPTH,
atlas->atlas, atlas->atlas,
BDEPTH*atlas->width); BDEPTH*atlas->width);

@ -24,13 +24,19 @@ struct font_glyph {
unsigned int u, v, w, h; 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); struct font_atlas * font_init(void);
int font_load(struct font_atlas *atlas, const char *path); int font_load(struct font_atlas *atlas, const char *path);
int font_free(struct font_atlas *atlas); 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); void font_dump(const struct font_atlas *atlas, const char *path);
#endif #endif

@ -1,33 +1,30 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <grapheme.h>
#include "cache.h" #include "ren.h"
#include "font.h"
#include "util.h" #include "util.h"
int main(void) int main(void)
{ {
int err; SDL_Window *win = SDL_CreateWindow(
struct font_atlas *at = font_init(); "test render",
err = font_load(at, "./monospace.ttf"); SDL_WINDOWPOS_UNDEFINED,
ERROR(err, -1, printf("failed to load font\n")); SDL_WINDOWPOS_UNDEFINED,
500,
const char *s = "ciao mamma"; 500,
const struct font_glyph *g; SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
size_t ret, off; if (ren_init(win)) {
uint_least32_t cp; printf("renderer init error: %s\n", ren_strerror());
for (off = 0; (ret = grapheme_decode_utf8(s+off, SIZE_MAX, &cp)) > 0 && cp != 0; off += ret) { return 1;
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");
} }
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; return 0;
} }

@ -1,53 +1,423 @@
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <GL/glew.h> #include <GL/glew.h>
#include <SDL2/SDL_opengl.h> #include <SDL2/SDL_opengl.h>
#include <SDL2/SDL_video.h>
#include <grapheme.h>
#include <stdio.h>
#include "util.h"
#include "font.h"
#include "ren.h"
#define GLSL_VERT_SHADER "vertex.glsl"
#define GLSL_FRAG_SHADER "fragment.glsl" enum REN_ERR {
#define PACKED __attribute__((packed)) 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 { struct {
SDL_Window *w;
SDL_GLContext *gl; SDL_GLContext *gl;
GLuint gl_vertbuffer; struct font_atlas *font;
GLuint gl_program;
GLuint font_texture; GLuint font_texture;
GLuint font_prog;
GLuint box_prog;
GLuint font_buffer;
struct vtstack font_stack;
struct vcstack box_stack;
} ren = {0}; } ren = {0};
typedef struct PACKED { static int ren_errno;
union { GLint x, u; };
union { GLint y, v; };
} vec2_i;
typedef struct PACKED {
union { GLint x, r; };
union { GLint y, g; };
union { GLint z, b; };
union { GLint w, a; };
} vec4_i;
// textured vertex // print shader compilation errors
struct PACKED v_text { static int shader_compile_error(GLuint shader)
vec2_i pos; {
vec2_i uv; GLint status;
}; glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status != GL_FALSE)
return 0;
// colored vertex GLint log_length;
struct PACKED v_col { glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
vec2_i pos;
vec2_i col;
};
GLchar *log_str = emalloc((log_length + 1)*sizeof(GLchar));
glGetShaderInfoLog(shader, log_length, NULL, log_str);
// different stacks const char *shader_type_str = NULL;
#define _STACK_NAME vtstack GLint shader_type;
#define _STACK_TYPE struct v_text glGetShaderiv(shader, GL_SHADER_TYPE, &shader_type);
#include "stack.h" switch(shader_type) {
#undef _STACK_NAME case GL_VERTEX_SHADER: shader_type_str = "vertex"; break;
#undef _STACK_TYPE case GL_GEOMETRY_SHADER: shader_type_str = "geometry"; break;
#define _STACK_NAME vcstack case GL_FRAGMENT_SHADER: shader_type_str = "fragment"; break;
#define _STACK_TYPE struct v_col }
#include "stack.h"
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;
}

@ -0,0 +1,48 @@
#ifndef _RENDERER_H
#define _RENDERER_H
#include <SDL2/SDL.h>
#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

@ -1,61 +1,72 @@
#ifdef _STACK_TYPE #ifndef _STACK_GENERIC_H
#ifdef _STACK_NAME #define _STACK_GENERIC_H
#ifndef _STACK_REALLOC #define STACK_STEP 8
#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
// TODO: add a rolling hash // TODO: add a rolling hash
struct _STACK_NAME { #define STACK_DECL(stackname, type) \
_STACK_TYPE *items; struct stackname { \
int size, idx; type *items; \
}; int size, idx; \
}; \
\
int _STACK_CAT(stack_grow_,_STACK_NAME)(struct _STACK_NAME *stack, int step) \
{ struct stackname stackname##_init(void) { return (struct stackname){0}; } \
if (!stack) \
return -1; \
stack->items = _STACK_REALLOC(stack->items, (stack->size+step)*sizeof(_STACK_TYPE)); int stackname##_grow(struct stackname *stack, int step) \
if(!stack->items) { \
return -1; if (!stack) \
_STACK_MEMSET(&(stack->items[stack->size]), 0, step*sizeof(*(stack->items))); return -1; \
stack->size += step; stack->items = realloc(stack->items, (stack->size+step)*sizeof(type)); \
return 0; 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 #endif

Binary file not shown.

@ -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) { if (!path) {
errno = EINVAL; errno = EINVAL;
@ -75,10 +75,9 @@ void dump_file(const char *path, unsigned char **buf, int *buf_len)
errno = EINVAL; errno = EINVAL;
err(EXIT_FAILURE, "No buffer specified"); err(EXIT_FAILURE, "No buffer specified");
} }
if (!buf_len) { int m = 0;
errno = EINVAL; if (!buf_len)
err(EXIT_FAILURE, "Nowhere to store buffer size"); buf_len = &m;
}
FILE *fp = fopen(path, "r"); FILE *fp = fopen(path, "r");
if (!fp) if (!fp)
err(EXIT_FAILURE, "Cannot open file %s", path); err(EXIT_FAILURE, "Cannot open file %s", path);

@ -10,7 +10,7 @@ void * ecalloc(unsigned long int nmemb, 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);
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); void print_byte(unsigned char byte);

Loading…
Cancel
Save