diff --git a/assets/ui/minogram.dtf b/assets/ui/minogram.dtf deleted file mode 100644 index cbf4aefb..00000000 Binary files a/assets/ui/minogram.dtf and /dev/null differ diff --git a/assets/ui/minogram.png b/assets/ui/minogram.png deleted file mode 100644 index c41f917a..00000000 Binary files a/assets/ui/minogram.png and /dev/null differ diff --git a/src/dusk/display/text/CMakeLists.txt b/src/dusk/display/text/CMakeLists.txt index 76a85df0..faca5a9a 100644 --- a/src/dusk/display/text/CMakeLists.txt +++ b/src/dusk/display/text/CMakeLists.txt @@ -7,4 +7,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC text.c + font.c ) \ No newline at end of file diff --git a/src/dusk/display/text/font.c b/src/dusk/display/text/font.c new file mode 100644 index 00000000..0c824e1a --- /dev/null +++ b/src/dusk/display/text/font.c @@ -0,0 +1,168 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "font.h" +#include "util/memory.h" +#include "util/math.h" +#include "display/color.h" + +font_t FONT_DEFAULT; +static texture_t FONT_DEFAULT_TEXTURE; +static tileset_t FONT_DEFAULT_TILESET; + +const uint8_t FONT_DEFAULT_GLYPHS[FONT_DEFAULT_TILE_COUNT][ + FONT_DEFAULT_TILE_HEIGHT +] = { + { 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x10, 0x00, 0x00 }, // ! + { 0x00, 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // " + { 0x00, 0x14, 0x14, 0x3E, 0x14, 0x3E, 0x14, 0x14, 0x00, 0x00 }, // # + { 0x00, 0x08, 0x1E, 0x28, 0x1C, 0x0A, 0x3C, 0x08, 0x00, 0x00 }, // $ + { 0x00, 0x00, 0x22, 0x24, 0x08, 0x12, 0x22, 0x00, 0x00, 0x00 }, // % + { 0x00, 0x08, 0x14, 0x14, 0x1A, 0x24, 0x24, 0x1A, 0x00, 0x00 }, // & + { 0x00, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // ' + { 0x00, 0x04, 0x08, 0x08, 0x08, 0x08, 0x08, 0x04, 0x00, 0x00 }, // ( + { 0x00, 0x10, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x00, 0x00 }, // ) + { 0x00, 0x00, 0x08, 0x2A, 0x1C, 0x2A, 0x08, 0x00, 0x00, 0x00 }, // * + { 0x00, 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x00, 0x00 }, // + + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x20, 0x00 }, // , + { 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00 }, // - + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00 }, // . + { 0x00, 0x04, 0x04, 0x08, 0x08, 0x08, 0x10, 0x10, 0x00, 0x00 }, // / + { 0x00, 0x1C, 0x22, 0x26, 0x2A, 0x32, 0x22, 0x1C, 0x00, 0x00 }, // 0 + { 0x00, 0x08, 0x18, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00 }, // 1 + { 0x00, 0x1C, 0x22, 0x02, 0x04, 0x08, 0x10, 0x3E, 0x00, 0x00 }, // 2 + { 0x00, 0x1C, 0x22, 0x02, 0x0C, 0x02, 0x22, 0x1C, 0x00, 0x00 }, // 3 + { 0x00, 0x22, 0x22, 0x22, 0x3E, 0x02, 0x02, 0x02, 0x00, 0x00 }, // 4 + { 0x00, 0x3E, 0x20, 0x20, 0x3C, 0x02, 0x22, 0x1C, 0x00, 0x00 }, // 5 + { 0x00, 0x1C, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x1C, 0x00, 0x00 }, // 6 + { 0x00, 0x3E, 0x02, 0x02, 0x04, 0x08, 0x08, 0x08, 0x00, 0x00 }, // 7 + { 0x00, 0x1C, 0x22, 0x22, 0x1C, 0x22, 0x22, 0x1C, 0x00, 0x00 }, // 8 + { 0x00, 0x1C, 0x22, 0x22, 0x1E, 0x02, 0x22, 0x1C, 0x00, 0x00 }, // 9 + { 0x00, 0x00, 0x10, 0x10, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00 }, // : + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // ; + { 0x00, 0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00 }, // < + { 0x00, 0x00, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00 }, // = + { 0x00, 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0x00, 0x00 }, // > + { 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. *