summaryrefslogtreecommitdiff
path: root/font.c
diff options
context:
space:
mode:
Diffstat (limited to 'font.c')
-rw-r--r--font.c238
1 files changed, 238 insertions, 0 deletions
diff --git a/font.c b/font.c
new file mode 100644
index 0000000..ba6dfb6
--- /dev/null
+++ b/font.c
@@ -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"