I can't even
This commit is contained in:
parent
805f633af0
commit
dcce0ed11b
@ -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
|
||||
|
Binary file not shown.
Before ![]() (image error) Size: 20 KiB After ![]() (image error) Size: 20 KiB ![]() ![]() |
@ -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)
|
||||
{
|
||||
const struct font_glyph *r;
|
||||
if ((r = cache_search(code)) != NULL)
|
||||
return r;
|
||||
int u = 0;
|
||||
if (!updated) updated = &u;
|
||||
|
||||
const struct font_glyph *r;
|
||||
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);
|
||||
|
@ -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
|
||||
|
@ -1,33 +1,30 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <grapheme.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
@ -1,53 +1,423 @@
|
||||
#include <SDL2/SDL.h>
|
||||
#include <GL/glew.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"
|
||||
#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;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
// colored vertex
|
||||
struct PACKED v_col {
|
||||
vec2_i pos;
|
||||
vec2_i col;
|
||||
};
|
||||
static int ren_errno;
|
||||
|
||||
|
||||
// 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"
|
||||
// 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;
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
48
text_rendering/ren.h
Normal file
48
text_rendering/ren.h
Normal file
@ -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
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
#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; \
|
||||
} \
|
||||
|
||||
|
||||
#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) {
|
||||
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);
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user