|
|
@ -2,6 +2,7 @@ |
|
|
|
#define STB_TRUETYPE_IMPLEMENTATION |
|
|
|
#define STB_TRUETYPE_IMPLEMENTATION |
|
|
|
#define STBTT_STATIC |
|
|
|
#define STBTT_STATIC |
|
|
|
#define MSDF_IMPLEMENTATION |
|
|
|
#define MSDF_IMPLEMENTATION |
|
|
|
|
|
|
|
#define STB_IMAGE_WRITE_IMPLEMENTATION |
|
|
|
|
|
|
|
|
|
|
|
#include <grapheme.h> |
|
|
|
#include <grapheme.h> |
|
|
|
#include <assert.h> |
|
|
|
#include <assert.h> |
|
|
@ -9,6 +10,7 @@ |
|
|
|
#include "font.h" |
|
|
|
#include "font.h" |
|
|
|
#include "msdf_c/stb_truetype.h" |
|
|
|
#include "msdf_c/stb_truetype.h" |
|
|
|
#include "msdf_c/msdf.h" |
|
|
|
#include "msdf_c/msdf.h" |
|
|
|
|
|
|
|
#include "stb_image_write.h" |
|
|
|
#include "util.h" |
|
|
|
#include "util.h" |
|
|
|
#include "cache.h" |
|
|
|
#include "cache.h" |
|
|
|
|
|
|
|
|
|
|
@ -49,28 +51,39 @@ static inline void * _emalloc(size_t x, void *_) { (void)_; return emalloc(x); } |
|
|
|
static inline void _efree(void *x, void *_) { (void)_; efree(x); } |
|
|
|
static inline void _efree(void *x, void *_) { (void)_; efree(x); } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct font_atlas * font_init(void) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return emalloc(sizeof(struct font_atlas)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// loads a font into memory, storing all the ASCII characters in the atlas
|
|
|
|
// loads a font into memory, storing all the ASCII characters in the atlas
|
|
|
|
int font_load(struct font_atlas *atlas, const char *path, int height) |
|
|
|
int font_load(struct font_atlas *atlas, const char *path) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (!atlas || !path) |
|
|
|
if (!atlas || !path) |
|
|
|
return -1; |
|
|
|
return -1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int err; |
|
|
|
|
|
|
|
|
|
|
|
dump_file(path, &(atlas->file), &(atlas->file_size)); |
|
|
|
dump_file(path, &(atlas->file), &(atlas->file_size)); |
|
|
|
|
|
|
|
|
|
|
|
stbtt_InitFont(&(atlas->priv.stb), atlas->file, 0); |
|
|
|
err = stbtt_InitFont(&(atlas->priv.stb), atlas->file, 0); |
|
|
|
atlas->priv.scale = stbtt_ScaleForPixelHeight(&(atlas->priv.stb), height); |
|
|
|
ERROR(err == 0, -1); |
|
|
|
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->priv.scale = stbtt_ScaleForPixelHeight(&(atlas->priv.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_w = (atlas->priv.scale*x1) - (atlas->priv.scale*x0);
|
|
|
|
//atlas->glyph_max_h = (baseline+atlas->priv.scale*y1) - (baseline+atlas->priv.scale*y0);
|
|
|
|
//atlas->glyph_max_h = (baseline+atlas->priv.scale*y1) - (baseline+atlas->priv.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 = ecalloc(CACHE_SIZE, BDEPTH*glyph_h*glyph_w); |
|
|
|
atlas->width = glyph_w*CACHE_SIZE; |
|
|
|
memset(atlas->atlas, 0, CACHE_SIZE*BDEPTH*glyph_h*glyph_w); |
|
|
|
atlas->height = glyph_h*CACHE_SIZE; |
|
|
|
atlas->width = glyph_w*CACHE_SIZE/2; |
|
|
|
|
|
|
|
atlas->height = glyph_h*CACHE_SIZE/2; |
|
|
|
|
|
|
|
|
|
|
|
atlas->priv.ctx = (msdf_AllocCtx){_emalloc, _efree, NULL}; |
|
|
|
atlas->priv.ctx = (msdf_AllocCtx){_emalloc, _efree, NULL}; |
|
|
|
|
|
|
|
|
|
|
@ -84,36 +97,60 @@ int font_free(struct font_atlas *atlas) |
|
|
|
{ |
|
|
|
{ |
|
|
|
efree(atlas->atlas); |
|
|
|
efree(atlas->atlas); |
|
|
|
efree(atlas->file); |
|
|
|
efree(atlas->file); |
|
|
|
|
|
|
|
efree(atlas); |
|
|
|
|
|
|
|
cache_destroy(); |
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
static unsigned int insert(struct font_atlas *atlas) |
|
|
|
const struct font_glyph * font_get_glyph_texture(struct font_atlas *atlas, unsigned int code) |
|
|
|
{ |
|
|
|
{ |
|
|
|
unsigned char *a = atlas->atlas; |
|
|
|
const struct font_glyph *r; |
|
|
|
msdf_Result res = atlas->priv.msdf; |
|
|
|
if ((r = cache_search(code)) != NULL) |
|
|
|
|
|
|
|
return r; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// generate the sdf and put it into the cache
|
|
|
|
|
|
|
|
// TODO: generate the whole block at once
|
|
|
|
|
|
|
|
int idx = stbtt_FindGlyphIndex(&atlas->priv.stb, code); |
|
|
|
|
|
|
|
// FIXME: what happens if I change the range?
|
|
|
|
|
|
|
|
int err; |
|
|
|
|
|
|
|
err = msdf_genGlyph(&atlas->priv.msdf, |
|
|
|
|
|
|
|
&atlas->priv.stb, |
|
|
|
|
|
|
|
idx, |
|
|
|
|
|
|
|
BORDER, |
|
|
|
|
|
|
|
atlas->priv.scale, |
|
|
|
|
|
|
|
2.0f/glyph_h, |
|
|
|
|
|
|
|
&atlas->priv.ctx); |
|
|
|
|
|
|
|
if (!err) |
|
|
|
|
|
|
|
return NULL; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
unsigned int spot = cache_get(); |
|
|
|
|
|
|
|
unsigned int oy = (glyph_h * spot) / atlas->width; |
|
|
|
|
|
|
|
unsigned int ox = (glyph_h * spot) % atlas->height; |
|
|
|
|
|
|
|
unsigned int w = atlas->width;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// sum magic shit
|
|
|
|
|
|
|
|
struct {unsigned char r,g,b;} *a = (void *)atlas->atlas; |
|
|
|
|
|
|
|
msdf_Result *res = &atlas->priv.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; |
|
|
|
float ts = 0.5 - ta / 2; |
|
|
|
float ts = 0.5 - ta / 2; |
|
|
|
float te = ts + ta; |
|
|
|
float te = ts + ta; |
|
|
|
|
|
|
|
|
|
|
|
unsigned int spot = cache_get(); |
|
|
|
|
|
|
|
unsigned int off = spot * glyph_h * glyph_w * BDEPTH; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define NORMALIZE(x) x = x > ts ? (x > te ? 1.0f : (x-ts)/ta+0.0f) : (0.0f) |
|
|
|
#define NORMALIZE(x) x = x > ts ? (x > te ? 1.0f : (x-ts)/ta+0.0f) : (0.0f) |
|
|
|
|
|
|
|
|
|
|
|
for (int y = 0; y < res.height; y++) { |
|
|
|
for (int y = 0; y < res->height; y++) { |
|
|
|
int yPos = res.width * 3 * y; |
|
|
|
int yPos = res->width * 3 * y; |
|
|
|
for (int x = 0; x < res.width; x++) { |
|
|
|
for (int x = 0; x < res->width; x++) { |
|
|
|
|
|
|
|
|
|
|
|
int i = yPos + (x * 3); |
|
|
|
int i = yPos + (x * 3); |
|
|
|
float r = res.rgb[i+0]; |
|
|
|
float r = res->rgb[i+0]; |
|
|
|
float g = res.rgb[i+1]; |
|
|
|
float g = res->rgb[i+1]; |
|
|
|
float b = res.rgb[i+2]; |
|
|
|
float b = res->rgb[i+2]; |
|
|
|
|
|
|
|
|
|
|
|
r = (r + s) / (s * 2.0f); |
|
|
|
r = (r + s) / (s * 2.0f); |
|
|
|
g = (g + s) / (s * 2.0f); |
|
|
|
g = (g + s) / (s * 2.0f); |
|
|
@ -123,36 +160,31 @@ static unsigned int insert(struct font_atlas *atlas) |
|
|
|
NORMALIZE(g); |
|
|
|
NORMALIZE(g); |
|
|
|
NORMALIZE(b); |
|
|
|
NORMALIZE(b); |
|
|
|
|
|
|
|
|
|
|
|
a[off + glyph_w*y + x + 0] = r * 255.0f; // (r > 0.5f) ? 255.0f : r * 255.0f;
|
|
|
|
a[(oy+y)*w + (ox+x)].r = r * 255.0f; // (r > 0.5f) ? 255.0f : r * 255.0f;
|
|
|
|
a[off + glyph_w*y + x + 0] = g * 255.0f; // (g > 0.5f) ? 255.0f : g * 255.0f;
|
|
|
|
a[(oy+y)*w + (ox+x)].g = g * 255.0f; // (g > 0.5f) ? 255.0f : g * 255.0f;
|
|
|
|
a[off + glyph_w*y + x + 0] = b * 255.0f; // (b > 0.5f) ? 255.0f : b * 255.0f;
|
|
|
|
a[(oy+y)*w + (ox+x)].b = b * 255.0f; // (b > 0.5f) ? 255.0f : b * 255.0f;
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return spot; |
|
|
|
struct font_glyph g = { |
|
|
|
|
|
|
|
.codepoint = code, |
|
|
|
|
|
|
|
.u = ox, |
|
|
|
|
|
|
|
.v = oy, |
|
|
|
|
|
|
|
.w = res->width, |
|
|
|
|
|
|
|
.h = res->height, |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
return cache_insert(&g, spot); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct font_glyph * font_get_glyph_texture(struct font_atlas *atlas, unsigned int code) |
|
|
|
void font_dump(const struct font_atlas *atlas, const char *path) |
|
|
|
{ |
|
|
|
{ |
|
|
|
struct font_glyph *r; |
|
|
|
stbi_write_png( |
|
|
|
if ((r = cache_search(code)) != NULL) |
|
|
|
path,
|
|
|
|
return r; |
|
|
|
//atlas->width,
|
|
|
|
|
|
|
|
//atlas->height,
|
|
|
|
// generate the sdf and put it into the cache
|
|
|
|
128, 128, |
|
|
|
// TODO: generate the whole block at once
|
|
|
|
BDEPTH, |
|
|
|
int idx = stbtt_FindGlyphIndex(&atlas->priv.stb, code); |
|
|
|
atlas->atlas, |
|
|
|
// FIXME: what happens if I change the range?
|
|
|
|
BDEPTH*atlas->width); |
|
|
|
int s; |
|
|
|
|
|
|
|
s = msdf_genGlyph(&atlas->priv.msdf, |
|
|
|
|
|
|
|
&atlas->priv.stb, |
|
|
|
|
|
|
|
idx, |
|
|
|
|
|
|
|
BORDER, |
|
|
|
|
|
|
|
atlas->priv.scale, |
|
|
|
|
|
|
|
2.0f/glyph_h, |
|
|
|
|
|
|
|
&atlas->priv.ctx); |
|
|
|
|
|
|
|
if (!s) |
|
|
|
|
|
|
|
return NULL; |
|
|
|
|
|
|
|
unsigned int spot = insert(atlas); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|