vtgl

terminal emulator implemented in OpenGL
git clone anongit@rnpnr.xyz:vtgl.git
Log | Files | Refs | Feed | LICENSE

Commit: 61602a6d403b230da75b6c6be9df7c79bca69eb3
Parent: 80f2a42dfebe74ca4d6a8f7488c3b7424d88b2b9
Author: Randy Palamar
Date:   Sat, 24 Aug 2024 11:33:44 -0600

unicode rendering

Diffstat:
Mfont.c | 47+++++++++++++++++++++++++++--------------------
Mterminal.c | 2--
Mutil.h | 43+++++++++++++++++++++++++++++--------------
Mvtgl.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};