summaryrefslogtreecommitdiff
path: root/kon
diff options
context:
space:
mode:
authorKonAki <konaki@anokonaki.org>2026-10-09 12:26:05 +0800
committerKonAki <konaki@anokonaki.org>2026-10-09 12:26:05 +0800
commitce29bee5026fc708d5aae23c9b95307bb7ad5bd1 (patch)
tree0c0af21717d89533ffb9bd84c621bb6bf1f5e6d4 /kon
parentc7961e8f5a3b94a7b2b319e169e1049796a6739b (diff)
Add built in bitmap font
Diffstat (limited to 'kon')
-rw-r--r--kon/konfont.h180
1 files changed, 178 insertions, 2 deletions
diff --git a/kon/konfont.h b/kon/konfont.h
index a75c502..3cd1006 100644
--- a/kon/konfont.h
+++ b/kon/konfont.h
@@ -41,6 +41,10 @@ typedef struct kon_font kon_font_t;
/*** font declarations ***/
kon_font_t *kon_loadFont(const uint8_t *ttf_data, size_t size, float pixelHeight);
+
+/* a small built in 8x13 bitmap font, needs no files. scale 1 is 13 pixels high, 2 is 26 and so on.
+ monospace, ascii only, the baseline is 10 rows below the top. kon_fontGetAtlas gives NULL for it */
+kon_font_t *kon_loadBuiltinFont(int scale);
void kon_freeFont(kon_font_t *font);
kon_image *kon_fontGetAtlas(kon_font_t *font);
@@ -61,6 +65,7 @@ void kon_measureText(kon_font_t *font, const char *text, int *outWidth, int *out
#include "stb_truetype.h"
struct kon_font {
+ int builtin, scale;
const uint8_t *ttf;
stbtt_bakedchar chardata[KON_FONT_NUM_CHARS];
uint8_t *atlasPixels;
@@ -68,6 +73,151 @@ struct kon_font {
float pixelHeight;
};
+/*** built in font ***/
+
+/* 8x13 glyphs for ascii 32 to 126, one byte per row, leftmost pixel is the top bit. public domain misc-fixed derived font */
+static const unsigned char kon_builtinGlyphs_[KON_FONT_NUM_CHARS][13] = {
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* */
+ { 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x10, 0x00, 0x00 }, /* ! */
+ { 0x00, 0x00, 0x24, 0x24, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* " */
+ { 0x00, 0x00, 0x00, 0x24, 0x24, 0x7E, 0x24, 0x7E, 0x24, 0x24, 0x00, 0x00, 0x00 }, /* # */
+ { 0x00, 0x00, 0x10, 0x3C, 0x50, 0x50, 0x38, 0x14, 0x14, 0x78, 0x10, 0x00, 0x00 }, /* $ */
+ { 0x00, 0x00, 0x22, 0x52, 0x24, 0x08, 0x08, 0x10, 0x24, 0x2A, 0x44, 0x00, 0x00 }, /* % */
+ { 0x00, 0x00, 0x00, 0x00, 0x30, 0x48, 0x48, 0x30, 0x4A, 0x44, 0x3A, 0x00, 0x00 }, /* & */
+ { 0x00, 0x00, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* ' */
+ { 0x00, 0x00, 0x04, 0x08, 0x08, 0x10, 0x10, 0x10, 0x08, 0x08, 0x04, 0x00, 0x00 }, /* ( */
+ { 0x00, 0x00, 0x20, 0x10, 0x10, 0x08, 0x08, 0x08, 0x10, 0x10, 0x20, 0x00, 0x00 }, /* ) */
+ { 0x00, 0x00, 0x00, 0x00, 0x24, 0x18, 0x7E, 0x18, 0x24, 0x00, 0x00, 0x00, 0x00 }, /* ch42 */
+ { 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x7C, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00 }, /* + */
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x30, 0x40, 0x00 }, /* , */
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* - */
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x10, 0x00 }, /* . */
+ { 0x00, 0x00, 0x02, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x80, 0x00, 0x00 }, /* ch47 */
+ { 0x00, 0x00, 0x18, 0x24, 0x42, 0x42, 0x42, 0x42, 0x42, 0x24, 0x18, 0x00, 0x00 }, /* 0 */
+ { 0x00, 0x00, 0x10, 0x30, 0x50, 0x10, 0x10, 0x10, 0x10, 0x10, 0x7C, 0x00, 0x00 }, /* 1 */
+ { 0x00, 0x00, 0x3C, 0x42, 0x42, 0x02, 0x04, 0x18, 0x20, 0x40, 0x7E, 0x00, 0x00 }, /* 2 */
+ { 0x00, 0x00, 0x7E, 0x02, 0x04, 0x08, 0x1C, 0x02, 0x02, 0x42, 0x3C, 0x00, 0x00 }, /* 3 */
+ { 0x00, 0x00, 0x04, 0x0C, 0x14, 0x24, 0x44, 0x44, 0x7E, 0x04, 0x04, 0x00, 0x00 }, /* 4 */
+ { 0x00, 0x00, 0x7E, 0x40, 0x40, 0x5C, 0x62, 0x02, 0x02, 0x42, 0x3C, 0x00, 0x00 }, /* 5 */
+ { 0x00, 0x00, 0x1C, 0x20, 0x40, 0x40, 0x5C, 0x62, 0x42, 0x42, 0x3C, 0x00, 0x00 }, /* 6 */
+ { 0x00, 0x00, 0x7E, 0x02, 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x00, 0x00 }, /* 7 */
+ { 0x00, 0x00, 0x3C, 0x42, 0x42, 0x42, 0x3C, 0x42, 0x42, 0x42, 0x3C, 0x00, 0x00 }, /* 8 */
+ { 0x00, 0x00, 0x3C, 0x42, 0x42, 0x46, 0x3A, 0x02, 0x02, 0x04, 0x38, 0x00, 0x00 }, /* 9 */
+ { 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x10, 0x00, 0x00, 0x10, 0x38, 0x10, 0x00 }, /* : */
+ { 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x10, 0x00, 0x00, 0x38, 0x30, 0x40, 0x00 }, /* ; */
+ { 0x00, 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, 0x00 }, /* < */
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00 }, /* = */
+ { 0x00, 0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x08, 0x10, 0x20, 0x40, 0x00, 0x00 }, /* > */
+ { 0x00, 0x00, 0x3C, 0x42, 0x42, 0x02, 0x04, 0x08, 0x08, 0x00, 0x08, 0x00, 0x00 }, /* ? */
+ { 0x00, 0x00, 0x3C, 0x42, 0x42, 0x4E, 0x52, 0x56, 0x4A, 0x40, 0x3C, 0x00, 0x00 }, /* @ */
+ { 0x00, 0x00, 0x18, 0x24, 0x42, 0x42, 0x42, 0x7E, 0x42, 0x42, 0x42, 0x00, 0x00 }, /* A */
+ { 0x00, 0x00, 0x78, 0x44, 0x42, 0x44, 0x78, 0x44, 0x42, 0x44, 0x78, 0x00, 0x00 }, /* B */
+ { 0x00, 0x00, 0x3C, 0x42, 0x40, 0x40, 0x40, 0x40, 0x40, 0x42, 0x3C, 0x00, 0x00 }, /* C */
+ { 0x00, 0x00, 0x78, 0x44, 0x42, 0x42, 0x42, 0x42, 0x42, 0x44, 0x78, 0x00, 0x00 }, /* D */
+ { 0x00, 0x00, 0x7E, 0x40, 0x40, 0x40, 0x78, 0x40, 0x40, 0x40, 0x7E, 0x00, 0x00 }, /* E */
+ { 0x00, 0x00, 0x7E, 0x40, 0x40, 0x40, 0x78, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00 }, /* F */
+ { 0x00, 0x00, 0x3C, 0x42, 0x40, 0x40, 0x40, 0x4E, 0x42, 0x46, 0x3A, 0x00, 0x00 }, /* G */
+ { 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00 }, /* H */
+ { 0x00, 0x00, 0x7C, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x7C, 0x00, 0x00 }, /* I */
+ { 0x00, 0x00, 0x1F, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x44, 0x38, 0x00, 0x00 }, /* J */
+ { 0x00, 0x00, 0x42, 0x44, 0x48, 0x50, 0x60, 0x50, 0x48, 0x44, 0x42, 0x00, 0x00 }, /* K */
+ { 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7E, 0x00, 0x00 }, /* L */
+ { 0x00, 0x00, 0x82, 0x82, 0xC6, 0xAA, 0x92, 0x92, 0x82, 0x82, 0x82, 0x00, 0x00 }, /* M */
+ { 0x00, 0x00, 0x42, 0x42, 0x62, 0x52, 0x4A, 0x46, 0x42, 0x42, 0x42, 0x00, 0x00 }, /* N */
+ { 0x00, 0x00, 0x3C, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3C, 0x00, 0x00 }, /* O */
+ { 0x00, 0x00, 0x7C, 0x42, 0x42, 0x42, 0x7C, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00 }, /* P */
+ { 0x00, 0x00, 0x3C, 0x42, 0x42, 0x42, 0x42, 0x42, 0x52, 0x4A, 0x3C, 0x02, 0x00 }, /* Q */
+ { 0x00, 0x00, 0x7C, 0x42, 0x42, 0x42, 0x7C, 0x50, 0x48, 0x44, 0x42, 0x00, 0x00 }, /* R */
+ { 0x00, 0x00, 0x3C, 0x42, 0x40, 0x40, 0x3C, 0x02, 0x02, 0x42, 0x3C, 0x00, 0x00 }, /* S */
+ { 0x00, 0x00, 0xFE, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00 }, /* T */
+ { 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3C, 0x00, 0x00 }, /* U */
+ { 0x00, 0x00, 0x82, 0x82, 0x44, 0x44, 0x44, 0x28, 0x28, 0x28, 0x10, 0x00, 0x00 }, /* V */
+ { 0x00, 0x00, 0x82, 0x82, 0x82, 0x82, 0x92, 0x92, 0x92, 0xAA, 0x44, 0x00, 0x00 }, /* W */
+ { 0x00, 0x00, 0x82, 0x82, 0x44, 0x28, 0x10, 0x28, 0x44, 0x82, 0x82, 0x00, 0x00 }, /* X */
+ { 0x00, 0x00, 0x82, 0x82, 0x44, 0x28, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00 }, /* Y */
+ { 0x00, 0x00, 0x7E, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x40, 0x7E, 0x00, 0x00 }, /* Z */
+ { 0x00, 0x00, 0x3C, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x00, 0x00 }, /* [ */
+ { 0x00, 0x00, 0x80, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x02, 0x00, 0x00 }, /* ch92 */
+ { 0x00, 0x00, 0x78, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x78, 0x00, 0x00 }, /* ] */
+ { 0x00, 0x00, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* ^ */
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00 }, /* _ */
+ { 0x00, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* ` */
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x3E, 0x42, 0x46, 0x3A, 0x00, 0x00 }, /* a */
+ { 0x00, 0x00, 0x40, 0x40, 0x40, 0x5C, 0x62, 0x42, 0x42, 0x62, 0x5C, 0x00, 0x00 }, /* b */
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x42, 0x40, 0x40, 0x42, 0x3C, 0x00, 0x00 }, /* c */
+ { 0x00, 0x00, 0x02, 0x02, 0x02, 0x3A, 0x46, 0x42, 0x42, 0x46, 0x3A, 0x00, 0x00 }, /* d */
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x42, 0x7E, 0x40, 0x42, 0x3C, 0x00, 0x00 }, /* e */
+ { 0x00, 0x00, 0x1C, 0x22, 0x20, 0x20, 0x7C, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00 }, /* f */
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x44, 0x44, 0x38, 0x40, 0x3C, 0x42, 0x3C }, /* g */
+ { 0x00, 0x00, 0x40, 0x40, 0x40, 0x5C, 0x62, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00 }, /* h */
+ { 0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x10, 0x10, 0x10, 0x10, 0x7C, 0x00, 0x00 }, /* i */
+ { 0x00, 0x00, 0x00, 0x04, 0x00, 0x0C, 0x04, 0x04, 0x04, 0x04, 0x44, 0x44, 0x38 }, /* j */
+ { 0x00, 0x00, 0x40, 0x40, 0x40, 0x44, 0x48, 0x70, 0x48, 0x44, 0x42, 0x00, 0x00 }, /* k */
+ { 0x00, 0x00, 0x30, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x7C, 0x00, 0x00 }, /* l */
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0x92, 0x92, 0x92, 0x92, 0x82, 0x00, 0x00 }, /* m */
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x5C, 0x62, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00 }, /* n */
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x42, 0x42, 0x42, 0x42, 0x3C, 0x00, 0x00 }, /* o */
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x5C, 0x62, 0x42, 0x62, 0x5C, 0x40, 0x40, 0x40 }, /* p */
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x46, 0x42, 0x46, 0x3A, 0x02, 0x02, 0x02 }, /* q */
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x5C, 0x22, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00 }, /* r */
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x42, 0x30, 0x0C, 0x42, 0x3C, 0x00, 0x00 }, /* s */
+ { 0x00, 0x00, 0x00, 0x20, 0x20, 0x7C, 0x20, 0x20, 0x20, 0x22, 0x1C, 0x00, 0x00 }, /* t */
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x3A, 0x00, 0x00 }, /* u */
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x28, 0x28, 0x10, 0x00, 0x00 }, /* v */
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x82, 0x92, 0x92, 0xAA, 0x44, 0x00, 0x00 }, /* w */
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x00, 0x00 }, /* x */
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x46, 0x3A, 0x02, 0x42, 0x3C }, /* y */
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x04, 0x08, 0x10, 0x20, 0x7E, 0x00, 0x00 }, /* z */
+ { 0x00, 0x00, 0x0E, 0x10, 0x10, 0x08, 0x30, 0x08, 0x10, 0x10, 0x0E, 0x00, 0x00 }, /* { */
+ { 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00 }, /* | */
+ { 0x00, 0x00, 0x70, 0x08, 0x08, 0x10, 0x0C, 0x10, 0x08, 0x08, 0x70, 0x00, 0x00 }, /* } */
+ { 0x00, 0x00, 0x24, 0x54, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* ~ */
+};
+
+#define KON_BUILTIN_WIDTH 8
+#define KON_BUILTIN_HEIGHT 13
+#define KON_BUILTIN_BASELINE 10
+#define KON_BUILTIN_MAX_SCALE 8
+
+static void kon_fontDrawBuiltin_(kon_framebuffer_t *fb, const kon_font_t *font, int x, int y, const char *text, uint32_t color) {
+ int s = font->scale;
+ int top = y - KON_BUILTIN_BASELINE * s;
+
+ color = (color & 0x00FFFFFFu) | 0xFF000000u;
+
+ for (const char *p = text; *p; p++) {
+ unsigned char c = (unsigned char)*p;
+ if (c < KON_FONT_FIRST_CHAR || c >= KON_FONT_FIRST_CHAR + KON_FONT_NUM_CHARS) continue;
+
+ for (int row = 0; row < KON_BUILTIN_HEIGHT; row++) {
+ unsigned char bits = kon_builtinGlyphs_[c - KON_FONT_FIRST_CHAR][row];
+
+ for (int bit = 0; bit < KON_BUILTIN_WIDTH; ) {
+ if (!(bits & (0x80 >> bit))) {
+ bit++;
+ continue;
+ }
+ int start = bit;
+ while (bit < KON_BUILTIN_WIDTH && (bits & (0x80 >> bit))) bit++;
+ kon_fillRectangle(fb, x + start * s, top + row * s, (bit - start) * s, s, color);
+ }
+ }
+ x += KON_BUILTIN_WIDTH * s;
+ }
+}
+
+kon_font_t *kon_loadBuiltinFont(int scale) {
+ if (scale < 1 || scale > KON_BUILTIN_MAX_SCALE) return NULL;
+
+ kon_font_t *font = calloc(1, sizeof(kon_font_t));
+ if (!font) return NULL;
+
+ font->builtin = 1;
+ font->scale = scale;
+ font->pixelHeight = (float)(KON_BUILTIN_HEIGHT * scale);
+ return font;
+}
+
/*** private helpers ***/
static bool kon_fontBakeAtlas(kon_font_t *font, const uint8_t *ttf_data, float pixelHeight) {
@@ -100,7 +250,7 @@ kon_font_t *kon_loadFont(const uint8_t *ttf_data, size_t size, float pixelHeight
if (!ttf_data || pixelHeight <= 0.0f) return NULL;
- kon_font_t *font = malloc(sizeof(kon_font_t));
+ kon_font_t *font = calloc(1, sizeof(kon_font_t));
if (!font) return NULL;
font->ttf = ttf_data;
@@ -131,6 +281,15 @@ int kon_fontResize(kon_font_t *font, float pixelHeight) {
if (!font || pixelHeight <= 0.0f) return 0;
+ if (font->builtin) {
+ int scale = (int)(pixelHeight / (float)KON_BUILTIN_HEIGHT + 0.5f);
+ if (scale < 1) scale = 1;
+ if (scale > KON_BUILTIN_MAX_SCALE) scale = KON_BUILTIN_MAX_SCALE;
+ font->scale = scale;
+ font->pixelHeight = (float)(KON_BUILTIN_HEIGHT * scale);
+ return 1;
+ }
+
next = *font;
next.atlasPixels = NULL;
if (!kon_fontBakeAtlas(&next, font->ttf, pixelHeight)) return 0;
@@ -142,7 +301,7 @@ int kon_fontResize(kon_font_t *font, float pixelHeight) {
}
kon_image *kon_fontGetAtlas(kon_font_t *font) {
- if (!font) return NULL;
+ if (!font || font->builtin) return NULL;
int n = font->atlasSize * font->atlasSize;
uint8_t *rgba = malloc((size_t)n * 4);
@@ -166,6 +325,11 @@ kon_image *kon_fontGetAtlas(kon_font_t *font) {
void kon_drawText(kon_framebuffer_t *fb, kon_font_t *font, int x, int y, const char *text, uint32_t color) {
if (!fb || !font || !text) return;
+ if (font->builtin) {
+ kon_fontDrawBuiltin_(fb, font, x, y, text, color);
+ return;
+ }
+
uint32_t baseColor = color & 0x00FFFFFF;
float cursorX = (float)x;
@@ -198,6 +362,18 @@ void kon_drawText(kon_framebuffer_t *fb, kon_font_t *font, int x, int y, const c
void kon_measureText(kon_font_t *font, const char *text, int *outWidth, int *outHeight) {
if (!font || !text) return;
+ if (font->builtin) {
+ int count = 0;
+
+ for (const char *p = text; *p; p++) {
+ unsigned char c = (unsigned char)*p;
+ if (c >= KON_FONT_FIRST_CHAR && c < KON_FONT_FIRST_CHAR + KON_FONT_NUM_CHARS) count++;
+ }
+ if (outWidth) *outWidth = count * KON_BUILTIN_WIDTH * font->scale;
+ if (outHeight) *outHeight = KON_BUILTIN_HEIGHT * font->scale;
+ return;
+ }
+
float cursorX = 0.0f;
float cursorY = 0.0f;
float maxX = 0.0f;