diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index 28dd37bbb..972647dd8 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -148,6 +148,10 @@ void canvas_invert_color(Canvas* canvas) { void canvas_set_font(Canvas* canvas, Font font) { furi_assert(canvas); u8g2_SetFontMode(&canvas->fb, 1); + if((FontSwap)font < FontSwapCount && xtreme_assets.fonts[font]) { + u8g2_SetFont(&canvas->fb, xtreme_assets.fonts[font]); + return; + } switch(font) { case FontPrimary: u8g2_SetFont(&canvas->fb, u8g2_font_helvB08_tr); diff --git a/lib/xtreme/assets.c b/lib/xtreme/assets.c index d087670a5..c3ba83a14 100644 --- a/lib/xtreme/assets.c +++ b/lib/xtreme/assets.c @@ -8,9 +8,11 @@ #define TAG "XtremeAssets" #define ICONS_FMT XTREME_ASSETS_PATH "/%s/Icons/%s" +#define FONTS_FMT XTREME_ASSETS_PATH "/%s/Fonts/%s.u8f" XtremeAssets xtreme_assets = { .is_nsfw = false, + .fonts = {NULL}, }; void load_icon_animated(const Icon* replace, const char* name, FuriString* path, File* file) { @@ -105,6 +107,29 @@ void free_icon(const Icon* icon) { free(frames); } +void load_font(FontSwap font, const char* name, FuriString* path, File* file) { + furi_string_printf(path, FONTS_FMT, xtreme_settings.asset_pack, name); + if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { + uint64_t size = storage_file_size(file); + uint8_t* swap = malloc(size); + + if(storage_file_read(file, swap, size) == size) { + xtreme_assets.fonts[font] = swap; + } else { + free(swap); + } + } + storage_file_close(file); +} + +static const char* font_names[] = { + [FontSwapPrimary] = "Primary", + [FontSwapSecondary] = "Secondary", + [FontSwapKeyboard] = "Keyboard", + [FontSwapBigNumbers] = "BigNumbers", + [FontSwapBatteryPercent] = "BatteryPercent", +}; + void XTREME_ASSETS_LOAD() { const char* pack = xtreme_settings.asset_pack; xtreme_assets.is_nsfw = !strncmp(pack, "NSFW", strlen("NSFW")); @@ -128,6 +153,10 @@ void XTREME_ASSETS_LOAD() { } } + for(FontSwap font = 0; font < FontSwapCount; font++) { + load_font(font, font_names[font], p, f); + } + storage_file_free(f); } furi_string_free(p); @@ -140,4 +169,11 @@ void XTREME_ASSETS_FREE() { free_icon(ICON_PATHS[i].icon); } } + + for(FontSwap font = 0; font < FontSwapCount; font++) { + if(xtreme_assets.fonts[font] != NULL) { + free(xtreme_assets.fonts[font]); + xtreme_assets.fonts[font] = NULL; + } + } } diff --git a/lib/xtreme/xtreme.h b/lib/xtreme/xtreme.h index 0e0c573c1..e9f1d59df 100644 --- a/lib/xtreme/xtreme.h +++ b/lib/xtreme/xtreme.h @@ -89,8 +89,18 @@ typedef struct { UARTChannel uart_general_channel; } XtremeSettings; +typedef enum { + FontSwapPrimary, + FontSwapSecondary, + FontSwapKeyboard, + FontSwapBigNumbers, + FontSwapBatteryPercent, + FontSwapCount, +} FontSwap; + typedef struct { bool is_nsfw; // TODO: replace with packs text support + uint8_t* fonts[FontSwapCount]; } XtremeAssets; void XTREME_SETTINGS_LOAD(); diff --git a/scripts/asset_packer.py b/scripts/asset_packer.py index d7cee4c7a..f6f9dacb5 100755 --- a/scripts/asset_packer.py +++ b/scripts/asset_packer.py @@ -85,6 +85,20 @@ def pack_icon_static(src: pathlib.Path, dst: pathlib.Path): dst.with_suffix(".bmx").write_bytes(convert_bmx(src)) +def pack_font(src: pathlib.Path, dst: pathlib.Path): + code = src.read_bytes().split(b' U8G2_FONT_SECTION("')[1].split(b'") =')[1].strip() + font = b"" + for line in code.splitlines(): + if line.count(b'"') == 2: + font += ( + line[line.find(b'"') + 1 : line.rfind(b'"')] + .decode("unicode_escape") + .encode("latin_1") + ) + dst.parent.mkdir(parents=True, exist_ok=True) + dst.with_suffix(".u8f").write_bytes(font) + + def pack( input: "str | pathlib.Path", output: "str | pathlib.Path", logger: typing.Callable ): @@ -145,6 +159,13 @@ def pack( icon, packed / "Icons" / icons.name / icon.name ) + if (source / "Fonts").is_dir(): + for font in (source / "Fonts").iterdir(): + if not font.is_file(): + continue + logger(f"Compile: font for pack '{source.name}': {font.name}") + pack_font(font, packed / "Fonts" / font.name) + if __name__ == "__main__": input( diff --git a/scripts/fbt_tools/fbt_assets.py b/scripts/fbt_tools/fbt_assets.py index 21a06eb1f..344e42625 100644 --- a/scripts/fbt_tools/fbt_assets.py +++ b/scripts/fbt_tools/fbt_assets.py @@ -85,6 +85,10 @@ def _packs_emitter(target, source, env): target_dir.File(source_dir.rel_path(node).removesuffix(".png") + ".bmx") for node in env.GlobRecursive("*/Icons/**/*.png", source_dir.srcnode()) ) + target.extend( + target_dir.File(source_dir.rel_path(node).removesuffix(".c") + ".u8f") + for node in env.GlobRecursive("*/Fonts/*.c", source_dir.srcnode()) + ) return target, source