master
Alessandro Mauri 1 year ago
parent 7552d2fa2a
commit 4b6f76b6c8
  1. 22
      text_rendering/cache.c
  2. 7
      text_rendering/cache.h
  3. 118
      text_rendering/font.c
  4. 11
      text_rendering/font.h
  5. 8
      text_rendering/msdf_c/CMakeLists.txt
  6. 22
      text_rendering/msdf_c/LICENSE

@ -6,6 +6,7 @@
#include <stdint.h>
#include <string.h>
#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;
}

@ -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

@ -1,17 +1,21 @@
#define _POSIX_C_SOURCE 200809l
#define STB_TRUETYPE_IMPLEMENTATION
#define STBTT_STATIC
#define MSDF_IMPLEMENTATION
#include <grapheme.h>
#include <assert.h>
#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);
}

@ -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

@ -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)

@ -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.
Loading…
Cancel
Save