Commit: 61602a6d403b230da75b6c6be9df7c79bca69eb3
Parent: 80f2a42dfebe74ca4d6a8f7488c3b7424d88b2b9
Author: Randy Palamar
Date: Sat, 24 Aug 2024 11:33:44 -0600
unicode rendering
Diffstat:
M | font.c | | | 47 | +++++++++++++++++++++++++++-------------------- |
M | terminal.c | | | 2 | -- |
M | util.h | | | 43 | +++++++++++++++++++++++++++++-------------- |
M | vtgl.c | | | 5 | ----- |
4 files changed, 56 insertions(+), 41 deletions(-)
diff --git a/font.c b/font.c
@@ -56,11 +56,18 @@ static u32 *
render_glyph(FontAtlas *fa, Arena a, u32 cp, Glyph *out_glyph, i32 *out_idx)
{
/* NOTE: first check if glyph is in the cache and valid */
- ASSERT(BETWEEN(cp, ' ', '~'));
- *out_idx = cp - ' ';
- if (fa->glyph_cache[cp - ' '].size.w > 0) {
- *out_glyph = fa->glyph_cache[cp - ' '];
+ /* TODO: better hash function! */
+ u32 idx = (((u64)cp * 1111111111111111111) >> 32) & (GLYPH_CACHE_LEN - 1);
+ *out_idx = idx;
+ CachedGlyph *cg = fa->glyph_cache.glyphs + idx;
+
+ if (cg->cp == cp) {
+ *out_glyph = cg->g;
return NULL;
+ } else {
+ if (cg->cp != 0)
+ cg->collision_count++;
+ cg->cp = cp;
}
FT_GlyphSlot gs = NULL;
@@ -78,21 +85,21 @@ render_glyph(FontAtlas *fa, Arena a, u32 cp, Glyph *out_glyph, i32 *out_idx)
gs = fa->fonts[0].face->glyph;
}
- out_glyph->size.w = gs->bitmap.width;
- out_glyph->size.h = gs->bitmap.rows;
- out_glyph->delta.x = gs->bitmap_left;
- out_glyph->delta.y = gs->bitmap_top - out_glyph->size.h;
+ cg->g.size.w = gs->bitmap.width;
+ cg->g.size.h = gs->bitmap.rows;
+ cg->g.delta.x = gs->bitmap_left;
+ cg->g.delta.y = gs->bitmap_top - cg->g.size.h;
u32 *rgba_bitmap = alloc(&a, u32, MAX_FONT_SIZE * MAX_FONT_SIZE);
- for (u32 i = 0; i < out_glyph->size.h; i++) {
- for (u32 j = 0; j < out_glyph->size.w; j++) {
+ for (u32 i = 0; i < cg->g.size.h; i++) {
+ for (u32 j = 0; j < cg->g.size.w; j++) {
/* TODO: handled coloured glyphs */
/* TODO: byte order of colour should be swapped to match GL_RBGA */
- Colour pixel = {.r = gs->bitmap.buffer[i * out_glyph->size.w + j]};
+ Colour pixel = {.r = gs->bitmap.buffer[i * cg->g.size.w + j]};
rgba_bitmap[i * MAX_FONT_SIZE + j] = pixel.rgba;
}
}
-
+ *out_glyph = cg->g;
return rgba_bitmap;
}
@@ -102,23 +109,24 @@ update_font_metrics(Term *t)
t->fa.size.h = 0;
t->fa.size.w = 0;
t->fa.deltay = 0;
+ Glyph g;
+ i32 index;
for (u32 i = ' '; i <= '~'; i++) {
- Glyph g;
- i32 _unused;
- render_glyph(&t->fa, t->arena_for_frame, i, &g, &_unused);
+ render_glyph(&t->fa, t->arena_for_frame, i, &g, &index);
t->fa.size.h = MAX(t->fa.size.h, g.size.h);
t->fa.size.w = MAX(t->fa.size.w, g.size.w);
t->fa.deltay = MAX(t->fa.deltay, -g.delta.y);
- t->fa.glyph_cache[i - ' '] = g;
}
/* NOTE: ' ' has 0 size in freetype but we need it to have a width! */
- t->fa.glyph_cache[0].size.w = t->fa.size.w;
+ render_glyph(&t->fa, t->arena_for_frame, ' ', &g, &index);
+ t->fa.glyph_cache.glyphs[index].g.size.w = t->fa.size.w;
}
static void
invalidate_font_cache(FontAtlas *fa)
{
- mem_clear((u8 *)fa->glyph_cache, 0, sizeof(*fa->glyph_cache) * fa->glyph_cache_len);
+ mem_clear((u8 *)fa->glyph_cache.glyphs, 0,
+ sizeof(*fa->glyph_cache.glyphs) * GLYPH_CACHE_LEN);
}
static i32
@@ -152,6 +160,5 @@ init_fonts(Term *t, Arena *a)
die("init_fonts: no valid font patterns\n");
}
- t->fa.glyph_cache_len = 0x7E - 0x20 + 1;
- t->fa.glyph_cache = alloc(a, Glyph, t->fa.glyph_cache_len);
+ t->fa.glyph_cache.glyphs = alloc(a, CachedGlyph, GLYPH_CACHE_LEN);
}
diff --git a/terminal.c b/terminal.c
@@ -986,8 +986,6 @@ push_line(Term *t, Line *line, Arena a)
if (wrap_next)
cursor_step_column(t, 1);
- /* TODO properly make sure characters are printable */
- CLAMP(cp, ' ', '~');
c = &tv->fb.rows[t->cursor.pos.y][t->cursor.pos.x];
c->cp = cp;
c->style = t->cursor.state;
diff --git a/util.h b/util.h
@@ -44,9 +44,11 @@
typedef float f32;
typedef double f64;
typedef uint8_t u8;
+typedef uint16_t u16;
typedef int32_t i32;
typedef uint32_t u32;
typedef uint32_t b32;
+typedef uint64_t u64;
typedef ptrdiff_t size;
typedef union {
@@ -163,13 +165,6 @@ typedef struct {
size last_line_idx;
} TermView;
-typedef struct {
- /* distance to shift glyph from bounding box origin */
- iv2 delta;
- /* rendered glyph bitmap data (pixels) */
- uv2 size;
-} Glyph;
-
#define GL_RENDER_UNIFORMS \
X(Pmat) \
X(charmap) \
@@ -267,14 +262,34 @@ typedef struct {
FT_Face face;
} Font;
+#define GLYPH_CACHE_LEN PUSH_BUFFER_CAP
+typedef struct {
+ /* distance to shift glyph from bounding box origin */
+ iv2 delta;
+ /* rendered glyph bitmap data (pixels) */
+ uv2 size;
+} Glyph;
+
+typedef struct {
+ Glyph g;
+ u32 cp;
+ u16 prev, next;
+ u32 collision_count;
+} CachedGlyph;
+
+typedef struct {
+ CachedGlyph *glyphs;
+ u32 mru_index;
+ u32 lru_index;
+} GlyphCache;
+
typedef struct {
- FT_Library ftlib;
- Font *fonts;
- u32 nfonts;
- uv2 size;
- i32 deltay;
- Glyph *glyph_cache;
- u32 glyph_cache_len;
+ FT_Library ftlib;
+ Font *fonts;
+ u32 nfonts;
+ uv2 size;
+ i32 deltay;
+ GlyphCache glyph_cache;
} FontAtlas;
typedef union {
diff --git a/vtgl.c b/vtgl.c
@@ -171,8 +171,6 @@ update_uniforms(Term *t, enum shader_stages stage)
static i32
get_gpu_glyph_index(Term *t, u32 codepoint, Glyph *out_glyph)
{
- /* TODO: will perform lookup in LRU cache */
- ASSERT(BETWEEN(codepoint, ' ', '~'));
i32 depth_idx;
u32 *data = render_glyph(&t->fa, t->arena_for_frame, codepoint, out_glyph, &depth_idx);
if (data) {
@@ -190,7 +188,6 @@ measure_text(GLCtx *gl, s8 text, b32 monospaced)
#if 0
f32 single_space_width = gl->glyph_cache[0].size.w;
for (size i = 0; i < text.len; i++) {
- ASSERT(BETWEEN(text.data[i], ' ', '~'));
Glyph g = gl->glyph_cache[text.data[i] - ' '];
/* TODO: should we consider offset characters in y? */
if (g.size.h > result.y)
@@ -256,9 +253,7 @@ draw_text(RenderPushBuffer *rpb, GLCtx *gl, s8 text, v2 position, Colour colour,
f32 single_space_width = gl->glyph_cache[0].size.w;
for (size i = 0; i < text.len; i++) {
Glyph g;
- /* TODO: don't do this */
u32 cp = text.data[i];
- CLAMP(cp, ' ', '~');
i32 glyph_idx = get_gpu_glyph_index(gl, cp, &g);
v2 texscale = {.x = g.size.w / MAX_FONT_SIZE, .y = g.size.h / MAX_FONT_SIZE};
v2 vertscale = {.x = g.size.w, .y = g.size.h};