You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ugui/text_rendering/font.c

195 lines
4.7 KiB

2 years ago
#define _POSIX_C_SOURCE 200809l
#define STB_TRUETYPE_IMPLEMENTATION
#define STBTT_STATIC
2 years ago
#define STB_IMAGE_WRITE_IMPLEMENTATION
2 years ago
#include <grapheme.h>
2 years ago
#include <assert.h>
2 years ago
2 years ago
#include "font.h"
#include "stb_truetype.h"
2 years ago
#include "stb_image_write.h"
2 years ago
#include "util.h"
2 years ago
#include "cache.h"
2 years ago
#define UTF8(c) (c&0x80)
#define BDEPTH 1
2 years ago
#define BORDER 4
2 years ago
// FIXME: as of now only monospaced fonts work correctly since no kerning information
// is stored
2 years ago
2 years ago
struct priv {
stbtt_fontinfo stb;
float scale;
int baseline;
unsigned char *bitmap;
2 years ago
};
#define PRIV(x) ((struct priv *)x->priv)
2 years ago
// 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); }
2 years ago
struct font_atlas * font_init(void)
{
2 years ago
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;
2 years ago
}
// loads a font into memory, storing all the ASCII characters in the atlas, each font
// atlas structure holds glyphs of a specific size
int font_load(struct font_atlas *atlas, const char *path, int size)
2 years ago
{
if (!atlas || !path)
return -1;
2 years ago
int err;
2 years ago
dump_file(path, &(atlas->file), &(atlas->file_size));
2 years ago
err = stbtt_InitFont(&(PRIV(atlas)->stb), (unsigned char *)atlas->file, 0);
2 years ago
ERROR(err == 0, -1);
2 years ago
int ascent, descent, linegap, baseline;
int x0,y0,x1,y1;
float scale;
scale = stbtt_ScaleForPixelHeight(&(PRIV(atlas)->stb), size);
stbtt_GetFontVMetrics(&(PRIV(atlas)->stb), &ascent, &descent, &linegap);
stbtt_GetFontBoundingBox(&(PRIV(atlas)->stb), &x0, &y0, &x1, &y1);
baseline = scale * -y0;
atlas->glyph_max_w = (scale*x1) - (scale*x0);
atlas->glyph_max_h = (baseline+scale*y1) - (baseline+scale*y0);
atlas->atlas = emalloc(CACHE_SIZE*BDEPTH*atlas->glyph_max_w*atlas->glyph_max_h);
memset(atlas->atlas, 0, CACHE_SIZE*BDEPTH*atlas->glyph_max_w*atlas->glyph_max_h);
PRIV(atlas)->baseline = atlas->glyph_max_h - baseline;
PRIV(atlas)->scale = scale;
PRIV(atlas)->bitmap = emalloc(BDEPTH*atlas->glyph_max_w*atlas->glyph_max_h);
2 years ago
// FIXME: make this a square atlas
atlas->width = atlas->glyph_max_w*CACHE_SIZE/4;
atlas->height = atlas->glyph_max_h*4;
atlas->size = size;
2 years ago
cache_init();
2 years ago
2 years ago
// preallocate all ascii characters
for (char c = ' '; c <= '~'; c++) {
if (!font_get_glyph_texture(atlas, c, NULL))
return -1;
}
2 years ago
return 0;
}
2 years ago
int font_free(struct font_atlas *atlas)
2 years ago
{
efree(atlas->atlas);
efree(atlas->file);
efree(PRIV(atlas)->bitmap);
2 years ago
efree(atlas->priv);
2 years ago
efree(atlas);
cache_destroy();
2 years ago
return 0;
}
2 years ago
// FIXME: when generating the sdf I only use the height, so to not encounter memory
// errors height and width must be equal
2 years ago
const struct font_glyph * font_get_glyph_texture(struct font_atlas *atlas, unsigned int code, int *updated)
2 years ago
{
2 years ago
int u = 0;
if (!updated) updated = &u;
2 years ago
const struct font_glyph *r;
2 years ago
if ((r = cache_search(code)) != NULL) {
*updated = 0;
2 years ago
return r;
2 years ago
}
2 years ago
2 years ago
*updated = 1;
2 years ago
// generate the sdf and put it into the cache
// TODO: generate the whole block at once
2 years ago
int idx = stbtt_FindGlyphIndex(&PRIV(atlas)->stb, code);
int x0,y0,x1,y1,gw,gh,l,off_x,off_y,adv;
stbtt_GetGlyphBitmapBoxSubpixel(
2 years ago
&PRIV(atlas)->stb,
2 years ago
idx,
2 years ago
PRIV(atlas)->scale,
PRIV(atlas)->scale,
0,0,
&x0,&y0,
&x1, &y1);
gw = x1 - x0;
gh = y1 - y0;
stbtt_GetGlyphHMetrics(&PRIV(atlas)->stb, idx, &adv, &l);
adv *= PRIV(atlas)->scale;
off_x = PRIV(atlas)->scale*l;
off_y = atlas->glyph_max_h+y0;
stbtt_MakeGlyphBitmapSubpixel(
&PRIV(atlas)->stb,
PRIV(atlas)->bitmap,
atlas->glyph_max_w,
atlas->glyph_max_h,
atlas->glyph_max_w,
PRIV(atlas)->scale,
PRIV(atlas)->scale,
0, 0,
idx);
2 years ago
// TODO: bounds check usign atlas height
// TODO: clear spot area in the atlas before writing on it
2 years ago
unsigned int spot = cache_get();
unsigned int oy = ((atlas->glyph_max_w * spot) / atlas->width) * atlas->glyph_max_h;
unsigned int ox = (atlas->glyph_max_w * spot) % atlas->width;
2 years ago
unsigned int w = atlas->width;
2 years ago
unsigned char *a = (void *)atlas->atlas;
//printf("max:%d %d spot:%d : %d %d %d %d\n", atlas->glyph_max_w, atlas->glyph_max_h, spot, ox, oy, off_x, off_y);
for (int y = 0; y < gh; y++) {
for (int x = 0; x < gw; x++) {
int c, r;
r = (oy+y)*w;
c = ox+x;
a[r+c] = PRIV(atlas)->bitmap[y*atlas->glyph_max_w+x];
2 years ago
}
}
// FIXME: get the advance
2 years ago
struct font_glyph g = {
.codepoint = code,
.u = ox,
.v = oy,
.w = gw,
.h = gh,
.x = off_x,
.y = off_y,
.a = adv,
2 years ago
};
2 years ago
const struct font_glyph *ret = cache_insert(&g, spot);
return ret;
2 years ago
}
2 years ago
void font_dump(const struct font_atlas *atlas, const char *path)
2 years ago
{
2 years ago
stbi_write_png(
2 years ago
path,
2 years ago
atlas->width,
atlas->height,
2 years ago
BDEPTH,
atlas->atlas,
BDEPTH*atlas->width);
2 years ago
}