diff --git a/text_rendering/cache.c b/text_rendering/cache.c index 7777908..f352549 100644 --- a/text_rendering/cache.c +++ b/text_rendering/cache.c @@ -6,6 +6,7 @@ #include #include +#include "cache.h" #include "hash.h" #include "font.h" #include "util.h" @@ -49,7 +50,7 @@ void cache_destroy(void) } -struct font_glyph * cache_get(unsigned int code) +const struct font_glyph * cache_search(unsigned int code) { struct hm_entry *r = hm_search(hash_table, code); @@ -63,21 +64,28 @@ struct font_glyph * cache_get(unsigned int code) } -int cache_insert(struct font_glyph *g) +// return the first free spot into the cache +unsigned int cache_get(void) { - struct font_glyph *spot = NULL; uint32_t x = 0; // find an open spot in the cache // TODO: use __builtin_clz to speed this up for (; x < CACHE_SIZE; x++) { -// printf("test: %d\n", x); if (!B_TEST(x)) break; } + return x; +} + + +// inserts the font glyph into the cache +const struct font_glyph * cache_insert(struct font_glyph *g, unsigned int x) +{ + struct font_glyph *spot = NULL; // allocation in cache failed if (B_TEST(x)) - return -1; + return NULL; set_bit(x); spot = &cache_array[x]; @@ -100,7 +108,7 @@ int cache_insert(struct font_glyph *g) struct hm_entry e = { .code = g->codepoint, .data = spot}; if (!hm_insert(hash_table, &e)) - return -1; + return NULL; - return 0; + return spot; } diff --git a/text_rendering/cache.h b/text_rendering/cache.h index 35923db..3741d47 100644 --- a/text_rendering/cache.h +++ b/text_rendering/cache.h @@ -1,10 +1,13 @@ #ifndef _CACHE_H #define _CACHE_H +#define CACHE_SIZE 512 + void cache_init(void); void cache_destroy(void); -struct font_glyph * cache_get(unsigned int code); -int cache_insert(struct font_glyph *g); +const struct font_glyph * cache_search(unsigned int code); +unsigned int cache_get(void); +const struct font_glyph * cache_insert(struct font_glyph *g, unsigned int idx); #endif diff --git a/text_rendering/font.c b/text_rendering/font.c index ecfba49..983cab5 100644 --- a/text_rendering/font.c +++ b/text_rendering/font.c @@ -1,17 +1,21 @@ #define _POSIX_C_SOURCE 200809l #define STB_TRUETYPE_IMPLEMENTATION #define STBTT_STATIC +#define MSDF_IMPLEMENTATION #include +#include +#include "font.h" #include "msdf_c/stb_truetype.h" #include "msdf_c/msdf.h" #include "util.h" -#include "font.h" +#include "cache.h" #define UTF8(c) (c&0x80) - +#define BDEPTH 3 +#define BORDER 4 // Generates a cached atlas of font glyphs encoded usign a signed distance field // https://www.youtube.com/watch?v=1b5hIMqz_wM @@ -21,13 +25,16 @@ struct font_atlas { - unsigned int glyphs; + unsigned int glyphs, width, height; unsigned char *atlas; struct { - stbtt_fontinfo info; + stbtt_fontinfo stb; + msdf_AllocCtx ctx; + msdf_Result msdf; float scale; - } stb; + } priv; + int file_size; unsigned char *file; }; @@ -37,34 +44,115 @@ const unsigned int glyph_w = 32; const unsigned int glyph_h = 32; +// 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); } + + // loads a font into memory, storing all the ASCII characters in the atlas -int load_font(struct font_atlas *atlas, const char *path, int height) +int font_load(struct font_atlas *atlas, const char *path, int height) { if (!atlas || !path) return -1; dump_file(path, &(atlas->file), &(atlas->file_size)); - stbtt_InitFont(&(atlas->stb.info), atlas->file, 0); - atlas->stb.scale = stbtt_ScaleForPixelHeight(&(atlas->stb.info), height); + stbtt_InitFont(&(atlas->priv.stb), atlas->file, 0); + atlas->priv.scale = stbtt_ScaleForPixelHeight(&(atlas->priv.stb), height); int ascent, descent, linegap, baseline; int x0,y0,x1,y1; - stbtt_GetFontVMetrics(&(atlas->stb.info), &ascent, &descent, &linegap); - stbtt_GetFontBoundingBox(&(atlas->stb.info), &x0, &y0, &x1, &y1); + stbtt_GetFontVMetrics(&(atlas->priv.stb), &ascent, &descent, &linegap); + stbtt_GetFontBoundingBox(&(atlas->priv.stb), &x0, &y0, &x1, &y1); - baseline = atlas->stb.scale * -y0; - //atlas->glyph_max_w = (atlas->stb.scale*x1) - (atlas->stb.scale*x0); - //atlas->glyph_max_h = (baseline+atlas->stb.scale*y1) - (baseline+atlas->stb.scale*y0); + 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); //atlas->atlas = emalloc(atlas->glyph_max_w*atlas->glyph_max_h*CACHE_SIZE); - atlas->atlas + atlas->atlas = ecalloc(CACHE_SIZE, BDEPTH*glyph_h*glyph_w); + atlas->width = glyph_w*CACHE_SIZE; + atlas->height = glyph_h*CACHE_SIZE; + + atlas->priv.ctx = (msdf_AllocCtx){_emalloc, _efree, NULL}; + + cache_init(); return 0; } -int free_font(struct font_atlas *atlas) +int font_free(struct font_atlas *atlas) { efree(atlas->atlas); efree(atlas->file); return 0; } + + +// FIXME: when generating the sdf I only use the height, so to not encounter memory +// errors height and width must be equal +static unsigned int insert(struct font_atlas *atlas) +{ + unsigned char *a = atlas->atlas; + msdf_Result res = atlas->priv.msdf; + + float s = glyph_h; + float tw = ((s * 0.7f) + s) / (s * 2.0f); + float ta = tw - 0.5f; + float ts = 0.5 - ta / 2; + 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) + + for (int y = 0; y < res.height; y++) { + int yPos = res.width * 3 * y; + for (int x = 0; x < res.width; x++) { + + int i = yPos + (x * 3); + float r = res.rgb[i+0]; + float g = res.rgb[i+1]; + float b = res.rgb[i+2]; + + r = (r + s) / (s * 2.0f); + g = (g + s) / (s * 2.0f); + b = (b + s) / (s * 2.0f); + + NORMALIZE(r); + NORMALIZE(g); + NORMALIZE(b); + + a[off + glyph_w*y + x + 0] = 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[off + glyph_w*y + x + 0] = b * 255.0f; // (b > 0.5f) ? 255.0f : b * 255.0f; + } + } + + return spot; +} + + +struct font_glyph * font_get_glyph_texture(struct font_atlas *atlas, unsigned int code) +{ + struct font_glyph *r; + 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 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); + +} diff --git a/text_rendering/font.h b/text_rendering/font.h index 3a0395a..5eee637 100644 --- a/text_rendering/font.h +++ b/text_rendering/font.h @@ -1,7 +1,6 @@ #ifndef _FONT_H #define _FONT_H -#define CACHE_SIZE 512 /* width and height of a glyph contain the kering advance * (u,v) @@ -28,13 +27,7 @@ struct font_glyph { struct font_atlas; -int load_font(struct font_atlas *atlas, const char *path, int height); -int free_font(struct font_atlas *atlas); - -void cache_init(void); -void cache_destroy(void); -struct font_glyph * cache_get(unsigned int code); -int cache_insert(struct font_glyph *g); - +int font_load(struct font_atlas *atlas, const char *path, int height); +int font_free(struct font_atlas *atlas); #endif diff --git a/text_rendering/msdf_c/CMakeLists.txt b/text_rendering/msdf_c/CMakeLists.txt deleted file mode 100644 index 6efc9f4..0000000 --- a/text_rendering/msdf_c/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -cmake_minimum_required(VERSION 3.19) -project(engine) - -add_definitions(-DSAMPLE_ROOT="${PROJECT_SOURCE_DIR}/sample") - -set(CMAKE_C_STANDARD 11) - -add_executable(msdf_sample sample/sample.c) \ No newline at end of file diff --git a/text_rendering/msdf_c/LICENSE b/text_rendering/msdf_c/LICENSE deleted file mode 100644 index 29cf0be..0000000 --- a/text_rendering/msdf_c/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -MIT License - -Copyright (c) 2023 Peter Jakobs -Copyright (c) 2018 exezin - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file