From e008fb108a7bb7f037362ee13a26a8f29e8a1ea1 Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Tue, 4 Aug 2026 22:37:22 -0500 Subject: [PATCH] Embed default font as a hard-coded bitmap instead of loading it as an asset Moves FONT_DEFAULT and its init/dispose into their own font.h/font.c, and rebuilds it from a static glyph bit array + a runtime-generated texture/tileset rather than loading ui/minogram.png/.dtf through the asset system. The engine now always has a usable font to render with, even if asset loading fails. The now-unused minogram assets are removed. Co-Authored-By: Claude Sonnet 5 --- assets/ui/minogram.dtf | Bin 24 -> 0 bytes assets/ui/minogram.png | Bin 1260 -> 0 bytes src/dusk/display/text/CMakeLists.txt | 1 + src/dusk/display/text/font.c | 168 +++++++++++++++++++++++++++ src/dusk/display/text/font.h | 49 ++++++++ src/dusk/display/text/text.c | 23 +--- src/dusk/display/text/text.h | 2 - 7 files changed, 220 insertions(+), 23 deletions(-) delete mode 100644 assets/ui/minogram.dtf delete mode 100644 assets/ui/minogram.png create mode 100644 src/dusk/display/text/font.c diff --git a/assets/ui/minogram.dtf b/assets/ui/minogram.dtf deleted file mode 100644 index cbf4aefb203a9e5d5576c901460bd8f8d9fcdb33..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24 dcmZ<>absX(;9?K}k_rqg3=9kowhRmkb^se+0t5g6 diff --git a/assets/ui/minogram.png b/assets/ui/minogram.png deleted file mode 100644 index c41f917ac2882308e41c4beb580f5753860562e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1260 zcmV z?Q*Oj42Gf8`@dz+pVG-m-+Vz3ba`fW++swO5Xp->t=Dyx!#ZlMOV9uHvz5Hy681~w zT3x@ei_^{FHh?dWywYz<`aDpBO;2lf1 ztz&BW+ezGJHmjqAcCA}4A%+O!4D0V0i(A^;y**2td?nkQI9N4GY=K*^M zY|-%z?;JpTJV+r9*=qJyl#p(;mO@5u^hoY5iCJ59Hp1fo){UWx9zU)K_cMr7X>olZGQUZduIRi`)Y zT|&kjVD?4Ks(+S7@F+YwKx3pMdXzx#LQ!vfqSvE|I^MdrOan{tNvR_tYa|WyI1fT= zZ?xkyBFf$tolc>PK}gQ^@YFDvDamfvv0i!}0h*%a9j(^LkFKuq?_R}{M}mx*`j(2H zL_K-&T6Xzu;Ne60I3-Xb`mW&Wk(&kmT>yXEMSBazV~FR^rw34?kq5fwPeWoXBF%f#CUhxRg_)@(9+*MfFA&Wu$r! zgpr?z&jfX}6!xt;4hrp+ut-SHwAVs9IyQ=>>_wJ?UkO(Nsg(Lre5ww>CxP#{s)L)e z7O;?DdnThJ`(KIlMo{E!9M4-io~*(1 z{(yB}scvn*l?iy-BDKeBzYJ+Dpb=RMkm}LwshzZzV6~@}YY{}+?@a^g_4TfmNcKy~ zSPt&Zqch*ji}JDQE+@kQJe|1PjoxkLav4Yoq(l~jqqWK<_Bkp2aJG=U=S@?D|Sr#zYc1euYX`IbVZ>92sFkl|wGz0J z$eiHy?*v}#Q`nYYe8e6ddsjJX=Ye1;fpnjwbTwuRJCYiw**7vWf|bJ6G`#;ZhJJ51 zV)m%YQV)@RPe^}V=ajZo~z$+<7&iTqt%fwOw5!$L2%DuJv(TJ8pJyPtn z`q3OgI*-L`9SQx%5KB(&(fze!G<)<2LVriv?`>C)-Q(C*35;?Szr*2hI2;a#0RI5C W$o0rJ!cj&50000 + { 0x00, 0x1C, 0x22, 0x02, 0x04, 0x08, 0x00, 0x08, 0x00, 0x00 }, // ? + { 0x00, 0x1C, 0x26, 0x2A, 0x2A, 0x26, 0x20, 0x1C, 0x00, 0x00 }, // @ + { 0x00, 0x1C, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x00, 0x00 }, // A + { 0x00, 0x3C, 0x22, 0x22, 0x3C, 0x22, 0x22, 0x3C, 0x00, 0x00 }, // B + { 0x00, 0x1C, 0x22, 0x20, 0x20, 0x20, 0x22, 0x1C, 0x00, 0x00 }, // C + { 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00 }, // D + { 0x00, 0x3E, 0x20, 0x20, 0x3C, 0x20, 0x20, 0x3E, 0x00, 0x00 }, // E + { 0x00, 0x3E, 0x20, 0x20, 0x3C, 0x20, 0x20, 0x20, 0x00, 0x00 }, // F + { 0x00, 0x1C, 0x22, 0x20, 0x2E, 0x22, 0x22, 0x1C, 0x00, 0x00 }, // G + { 0x00, 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, 0x00 }, // H + { 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00 }, // I + { 0x00, 0x02, 0x02, 0x02, 0x02, 0x22, 0x22, 0x1C, 0x00, 0x00 }, // J + { 0x00, 0x22, 0x24, 0x28, 0x30, 0x28, 0x24, 0x22, 0x00, 0x00 }, // K + { 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3E, 0x00, 0x00 }, // L + { 0x00, 0x22, 0x36, 0x2A, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00 }, // M + { 0x00, 0x22, 0x22, 0x32, 0x2A, 0x26, 0x22, 0x22, 0x00, 0x00 }, // N + { 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00 }, // O + { 0x00, 0x3C, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x20, 0x00, 0x00 }, // P + { 0x00, 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x06, 0x00 }, // Q + { 0x00, 0x3C, 0x22, 0x22, 0x3C, 0x22, 0x22, 0x22, 0x00, 0x00 }, // R + { 0x00, 0x1C, 0x22, 0x20, 0x1C, 0x02, 0x22, 0x1C, 0x00, 0x00 }, // S + { 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00 }, // T + { 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00 }, // U + { 0x00, 0x22, 0x22, 0x22, 0x22, 0x14, 0x14, 0x08, 0x00, 0x00 }, // V + { 0x00, 0x22, 0x22, 0x22, 0x22, 0x2A, 0x36, 0x22, 0x00, 0x00 }, // W + { 0x00, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x00, 0x00 }, // X + { 0x00, 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00 }, // Y + { 0x00, 0x3E, 0x02, 0x04, 0x08, 0x10, 0x20, 0x3E, 0x00, 0x00 }, // Z + { 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x0C, 0x00, 0x00 }, // [ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // backslash (not drawn in source font) + { 0x00, 0x18, 0x08, 0x08, 0x08, 0x08, 0x08, 0x18, 0x00, 0x00 }, // ] + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // ^ (not drawn in source font) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // _ (not drawn in source font) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // ` (not drawn in source font) + { 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00 }, // a + { 0x00, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x3C, 0x00, 0x00 }, // b + { 0x00, 0x00, 0x00, 0x1C, 0x22, 0x20, 0x22, 0x1C, 0x00, 0x00 }, // c + { 0x00, 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00 }, // d + { 0x00, 0x00, 0x00, 0x1C, 0x22, 0x3E, 0x20, 0x1C, 0x00, 0x00 }, // e + { 0x00, 0x0C, 0x12, 0x10, 0x3C, 0x10, 0x10, 0x10, 0x00, 0x00 }, // f + { 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x1C }, // g + { 0x00, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00 }, // h + { 0x00, 0x08, 0x00, 0x18, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00 }, // i + { 0x00, 0x02, 0x00, 0x06, 0x02, 0x02, 0x02, 0x02, 0x22, 0x1C }, // j + { 0x00, 0x20, 0x20, 0x22, 0x24, 0x38, 0x24, 0x22, 0x00, 0x00 }, // k + { 0x00, 0x30, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0E, 0x00, 0x00 }, // l + { 0x00, 0x00, 0x00, 0x3C, 0x2A, 0x2A, 0x2A, 0x2A, 0x00, 0x00 }, // m + { 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00 }, // n + { 0x00, 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x00, 0x00 }, // o + { 0x00, 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x20 }, // p + { 0x00, 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x02 }, // q + { 0x00, 0x00, 0x00, 0x2C, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00 }, // r + { 0x00, 0x00, 0x00, 0x1E, 0x20, 0x1C, 0x02, 0x3C, 0x00, 0x00 }, // s + { 0x00, 0x10, 0x10, 0x3C, 0x10, 0x10, 0x10, 0x0E, 0x00, 0x00 }, // t + { 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, 0x00 }, // u + { 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x08, 0x00, 0x00 }, // v + { 0x00, 0x00, 0x00, 0x22, 0x22, 0x2A, 0x2A, 0x14, 0x00, 0x00 }, // w + { 0x00, 0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x00, 0x00 }, // x + { 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x1C }, // y + { 0x00, 0x00, 0x00, 0x3E, 0x04, 0x08, 0x10, 0x3E, 0x00, 0x00 }, // z + { 0x00, 0x04, 0x08, 0x08, 0x10, 0x08, 0x08, 0x04, 0x00, 0x00 }, // { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // | (not drawn in source font) + { 0x00, 0x10, 0x08, 0x08, 0x04, 0x08, 0x08, 0x10, 0x00, 0x00 }, // } + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // ~ (not drawn in source font) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // unused trailing tile + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // unused trailing tile +}; + +errorret_t fontDefaultInit(void) { + const int32_t width = (int32_t)mathNextPowTwo( + FONT_DEFAULT_COLUMNS * FONT_DEFAULT_TILE_WIDTH + ); + const int32_t height = (int32_t)mathNextPowTwo( + FONT_DEFAULT_ROWS * FONT_DEFAULT_TILE_HEIGHT + ); + + color_t *pixels = memoryAllocate(sizeof(color_t) * width * height); + memoryZero(pixels, sizeof(color_t) * width * height); + + for(uint16_t i = 0; i < FONT_DEFAULT_TILE_COUNT; i++) { + const uint16_t tileX = (i % FONT_DEFAULT_COLUMNS) * FONT_DEFAULT_TILE_WIDTH; + const uint16_t tileY = (i / FONT_DEFAULT_COLUMNS) * FONT_DEFAULT_TILE_HEIGHT; + + for(uint8_t row = 0; row < FONT_DEFAULT_TILE_HEIGHT; row++) { + const uint8_t bits = FONT_DEFAULT_GLYPHS[i][row]; + + for(uint8_t col = 0; col < FONT_DEFAULT_TILE_WIDTH; col++) { + if(!((bits >> (FONT_DEFAULT_TILE_WIDTH - 1 - col)) & 1)) continue; + pixels[((tileY + row) * width) + (tileX + col)] = COLOR_WHITE; + } + } + } + + FONT_DEFAULT_TILESET.tileWidth = FONT_DEFAULT_TILE_WIDTH; + FONT_DEFAULT_TILESET.tileHeight = FONT_DEFAULT_TILE_HEIGHT; + FONT_DEFAULT_TILESET.columns = FONT_DEFAULT_COLUMNS; + FONT_DEFAULT_TILESET.rows = FONT_DEFAULT_ROWS; + FONT_DEFAULT_TILESET.tileCount = FONT_DEFAULT_TILE_COUNT; + FONT_DEFAULT_TILESET.uv[0] = (float_t)FONT_DEFAULT_TILE_WIDTH / (float_t)width; + FONT_DEFAULT_TILESET.uv[1] = (float_t)FONT_DEFAULT_TILE_HEIGHT / (float_t)height; + + const texturedata_t data = { .rgbaColors = pixels }; + errorret_t textureResult = textureInit( + &FONT_DEFAULT_TEXTURE, width, height, TEXTURE_FORMAT_RGBA, data + ); + memoryFree(pixels); + errorChain(textureResult); + + FONT_DEFAULT.texture = &FONT_DEFAULT_TEXTURE; + FONT_DEFAULT.tileset = &FONT_DEFAULT_TILESET; + errorOk(); +} + +errorret_t fontDefaultDispose(void) { + errorChain(textureDispose(&FONT_DEFAULT_TEXTURE)); + FONT_DEFAULT.texture = NULL; + FONT_DEFAULT.tileset = NULL; + errorOk(); +} diff --git a/src/dusk/display/text/font.h b/src/dusk/display/text/font.h index 9cdc7b3b..8efce342 100644 --- a/src/dusk/display/text/font.h +++ b/src/dusk/display/text/font.h @@ -6,6 +6,7 @@ */ #pragma once +#include "error/error.h" #include "display/texture/texture.h" #include "display/texture/tileset.h" @@ -13,3 +14,51 @@ typedef struct { texture_t *texture; tileset_t *tileset; } font_t; + +/** + * Pixel width/height of a single default-font glyph tile. + */ +#define FONT_DEFAULT_TILE_WIDTH 6 +#define FONT_DEFAULT_TILE_HEIGHT 10 + +/** Grid layout of the generated default-font texture, in tiles. */ +#define FONT_DEFAULT_COLUMNS 16 +#define FONT_DEFAULT_ROWS 6 + +/** + * Number of glyphs defined in FONT_DEFAULT_GLYPHS (FONT_DEFAULT_COLUMNS * + * FONT_DEFAULT_ROWS), covering the printable ASCII range starting at + * TEXT_CHAR_START ('!') plus a couple of unused trailing tiles. + */ +#define FONT_DEFAULT_TILE_COUNT (FONT_DEFAULT_COLUMNS * FONT_DEFAULT_ROWS) + +extern font_t FONT_DEFAULT; + +/** + * Hard coded bitmap data for the built-in default font. Indexed + * [glyph][row], where glyph 0 corresponds to TEXT_CHAR_START ('!') and + * glyphs run consecutively through the printable ASCII range. Each row + * byte holds FONT_DEFAULT_TILE_WIDTH bit flags, one per pixel column: + * bit (FONT_DEFAULT_TILE_WIDTH - 1) is the leftmost pixel and bit 0 is + * the rightmost; 1 means the pixel is set, 0 means it is not. + */ +extern const uint8_t FONT_DEFAULT_GLYPHS[FONT_DEFAULT_TILE_COUNT][ + FONT_DEFAULT_TILE_HEIGHT +]; + +/** + * Builds the default font's texture + tileset directly from + * FONT_DEFAULT_GLYPHS, without going through the asset system - so the + * engine always has a usable font to render with regardless of whether + * asset loading (e.g. the packed .dsk archive) succeeds. + * + * @return Either an error or success result. + */ +errorret_t fontDefaultInit(void); + +/** + * Disposes of the default font created by fontDefaultInit(). + * + * @return Either an error or success result. + */ +errorret_t fontDefaultDispose(void); diff --git a/src/dusk/display/text/text.c b/src/dusk/display/text/text.c index dde87683..a9210fa6 100644 --- a/src/dusk/display/text/text.c +++ b/src/dusk/display/text/text.c @@ -9,34 +9,15 @@ #include "assert/assert.h" #include "util/memory.h" #include "display/spritebatch/spritebatch.h" -#include "asset/asset.h" -#include "asset/loader/display/assettextureloader.h" -#include "asset/loader/display/assettilesetloader.h" #include "display/shader/shaderunlit.h" -font_t FONT_DEFAULT; - errorret_t textInit(void) { - assetloaderinput_t input = { .texture = TEXTURE_FORMAT_RGBA }; - assetentry_t *entryTexture = assetLock( - "ui/minogram.png", ASSET_LOADER_TYPE_TEXTURE, &input - ); - assetentry_t *entryTileset = assetLock( - "ui/minogram.dtf", ASSET_LOADER_TYPE_TILESET, NULL - ); - errorChain(assetRequireLoaded(entryTexture)); - errorChain(assetRequireLoaded(entryTileset)); - - FONT_DEFAULT.texture = &entryTexture->data.texture; - FONT_DEFAULT.tileset = &entryTileset->data.tileset; + errorChain(fontDefaultInit()); errorOk(); } errorret_t textDispose(void) { - FONT_DEFAULT.texture = NULL; - FONT_DEFAULT.tileset = NULL; - assetUnlock("ui/minogram.png"); - assetUnlock("ui/minogram.dtf"); + errorChain(fontDefaultDispose()); errorOk(); } diff --git a/src/dusk/display/text/text.h b/src/dusk/display/text/text.h index a62bf3bd..60f2fdfb 100644 --- a/src/dusk/display/text/text.h +++ b/src/dusk/display/text/text.h @@ -12,8 +12,6 @@ #define TEXT_CHAR_START '!' -extern font_t FONT_DEFAULT; - /** * Initializes the text system. *