diff options
author | quou <quou@disroot.org> | 2024-06-01 12:19:16 +1000 |
---|---|---|
committer | quou <quou@disroot.org> | 2024-06-01 12:20:17 +1000 |
commit | ea7cd94f7aeb177618db3907a6c86b7252e018f0 (patch) | |
tree | e972f9cf590ef756c2e41f3eac5b03e16db08300 /font.c |
Initial commit.
Diffstat (limited to 'font.c')
-rw-r--r-- | font.c | 238 |
1 files changed, 238 insertions, 0 deletions
@@ -0,0 +1,238 @@ +#include "memory.h" +#include "plat.h" +#include "render.h" +#include "stb_rect_pack.h" +#include "stb_truetype.h" +#include <math.h> + +typedef struct { + Bitmap bmp; + stbtt_bakedchar glyphs[max_glyphset]; +} Glyph_Set; + +struct Font { + const void* data; + Heap* heap; + stbtt_fontinfo info; + Glyph_Set* sets[max_glyphset]; + int size, height; +}; + +int font_height(Font* f) { + return f->height; +} + +static const char* utf8_to_codepoint( + const char* p, + unsigned* dst +) { + unsigned res, n; + switch (*p & 0xf0) { + case 0xf0 : res = *p & 0x07; n = 3; break; + case 0xe0 : res = *p & 0x0f; n = 2; break; + case 0xd0 : + case 0xc0 : res = *p & 0x1f; n = 1; break; + default : res = *p; n = 0; break; + } + while (n--) { + res = (res << 6) | (*(++p) & 0x3f); + } + *dst = res; + return p + 1; +} + +static Glyph_Set* load_glyph_set( + Font* font, + int idx +) { + Glyph_Set* gs; + Bitmap* bmp; + int r, i; + int ascent, descent, linegap, scaled_ascent; + float s; + gs = heap_alloc(font->heap, sizeof *gs); + bmp = &gs->bmp; + bmp->w = 512; + bmp->h = 512; +retry: + bmp->pixels = heap_alloc( + font->heap, + bmp->w * bmp->h * 4 + ); + s = + stbtt_ScaleForMappingEmToPixels(&font->info, 1) / + stbtt_ScaleForPixelHeight(&font->info, 1); + r = stbtt_BakeFontBitmap( + font->data, + 0, + font->size * s, + (unsigned char*)bmp->pixels, + bmp->w, + bmp->h, + idx * 256, + 256, + gs->glyphs + ); + if (r <= 0) { + bmp->w *= 2; + bmp->h *= 2; + heap_free(font->heap, bmp->pixels); + goto retry; + } + + stbtt_GetFontVMetrics(&font->info, &ascent, &descent, &linegap); + s = stbtt_ScaleForMappingEmToPixels(&font->info, font->size); + scaled_ascent = (int)(ascent * s + 0.5f); + for (i = 0; i < 256; i++) { + gs->glyphs[i].yoff += scaled_ascent; + gs->glyphs[i].xadvance = (float)floor(gs->glyphs[i].xadvance); + } + for (i = bmp->w * bmp->h - 1; i >= 0; i--) { + unsigned char n = ((unsigned char*)bmp->pixels)[i]; + bmp->pixels[i].r = 255; + bmp->pixels[i].g = 255; + bmp->pixels[i].b = 255; + bmp->pixels[i].a = n; + } + return gs; +} + +static Glyph_Set* get_glyph_set( + Font* font, + int codepoint +) { + int idx = (codepoint >> 8) % max_glyphset; + if (!font->sets[idx]) { + font->sets[idx] = load_glyph_set(font, idx); + } + return font->sets[idx]; +} + +void init_font( + Font* font, + Heap* heap, + const unsigned char* raw, + int size +) { + int r, i; + int ascent, descent, linegap; + float scale; + font->heap = heap; + font->data = raw; + font->size = size; + stbtt_bakedchar* g; + for (i = 0; i < max_glyphset; i++) + font->sets[i] = 0; + r = stbtt_InitFont(&font->info, font->data, 0); + if (!r) { + print("Invalid font.\n"); + pbreak(error_font); + } + stbtt_GetFontVMetrics( + &font->info, + &ascent, + &descent, + &linegap + ); + scale = stbtt_ScaleForMappingEmToPixels( + &font->info, + size + ); + font->height = (int)( + (ascent - descent + linegap) * + scale + 0.5f + ); + g = get_glyph_set(font, '\n')->glyphs; + g['\t'].x1 = g['\t'].x0; + g['\n'].x1 = g['\n'].x0; +} + +Font* new_font( + Heap* h, + const unsigned char* raw, + int size +) { + Font* f; + f = heap_alloc(h, sizeof *f); + init_font(f, h, raw, size); + return f; +} + +Rectangle text_rect( + Font* font, + const char* text +) { + const char* p = text; + unsigned codepoint; + int x = 0; + Glyph_Set* gs; + stbtt_bakedchar* g; + Rectangle re = { 0 }, r; + while (*p) { + p = utf8_to_codepoint(p, &codepoint); + gs = get_glyph_set(font, codepoint); + g = &gs->glyphs[codepoint & 0xff]; + r.x = x + (int)g->xoff; + r.y = (int)g->yoff; + r.w = (int)g->x1 - g->x0; + r.h = (int)g->y1 - g->y0; + rect_merge(&re, &r); + x += (int)g->xadvance; + } + return re; +} + +#define rf(expr) \ + const char* p = text; \ + unsigned codepoint; \ + Glyph_Set* gs; \ + stbtt_bakedchar* g; \ + Rectangle r; \ + while (*p) { \ + p = utf8_to_codepoint(p, &codepoint); \ + gs = get_glyph_set(font, codepoint); \ + g = &gs->glyphs[codepoint & 0xff]; \ + r.x = (int)g->x0; \ + r.y = (int)g->y0; \ + r.w = (int)g->x1 - g->x0; \ + r.h = (int)g->y1 - g->y0; \ + expr; \ + x += (int)g->xadvance; \ + } + +void rfont_text( + Font* font, + int x, + int y, + const char* text +) { + rf(render_bitmap( + &gs->bmp, + x + (int)g->xoff, + y + (int)g->yoff, + &r + )); +} + +void rfont_text_col( + Font* font, + int x, + int y, + const char* text, + Colour colour +) { + rf(render_bitmap_col( + &gs->bmp, + x + (int)g->xoff, + y + (int)g->yoff, + &r, + colour + )); +} + +#undef rf + +#define STB_RECT_PACK_IMPLEMENTATION +#define STB_TRUETYPE_IMPLEMENTATION +#include "stb_rect_pack.h" +#include "stb_truetype.h" |