Skip to content

Commit 3a9bd71

Browse files
committed
Add native support for LVGL binary fonts on disk
This allows the terminal to use a font from the disk at /fonts/terminal.lvfontbin for languages that need more characters than we want to ship. Font files for use with it are generated by the GitHub actions here: https://github.com/adafruit/circuitpython-font-generator
1 parent 70fe5cd commit 3a9bd71

File tree

25 files changed

+1679
-148
lines changed

25 files changed

+1679
-148
lines changed

locale/circuitpython.pot

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -985,6 +985,26 @@ msgstr ""
985985
msgid "Failed to allocate Wifi memory"
986986
msgstr ""
987987

988+
#: shared-module/lvfontio/BuiltinFont.c
989+
msgid "Failed to allocate bitmap buffer"
990+
msgstr ""
991+
992+
#: shared-module/lvfontio/BuiltinFont.c
993+
msgid "Failed to allocate bitmap object"
994+
msgstr ""
995+
996+
#: shared-module/lvfontio/BuiltinFont.c
997+
msgid "Failed to allocate cmap ranges"
998+
msgstr ""
999+
1000+
#: shared-module/lvfontio/BuiltinFont.c
1001+
msgid "Failed to allocate codepoints array"
1002+
msgstr ""
1003+
1004+
#: shared-module/lvfontio/BuiltinFont.c
1005+
msgid "Failed to allocate reference counts"
1006+
msgstr ""
1007+
9881008
#: ports/espressif/common-hal/wifi/ScannedNetworks.c
9891009
msgid "Failed to allocate wifi scan memory"
9901010
msgstr ""
@@ -1022,6 +1042,11 @@ msgstr ""
10221042
msgid "Failed to enable continuous"
10231043
msgstr ""
10241044

1045+
#: shared-module/lvfontio/BuiltinFont.c
1046+
#, c-format
1047+
msgid "Failed to open file %d"
1048+
msgstr ""
1049+
10251050
#: shared-module/audiomp3/MP3Decoder.c
10261051
msgid "Failed to parse MP3 file"
10271052
msgstr ""
@@ -1074,6 +1099,10 @@ msgstr ""
10741099
msgid "Firmware is too big"
10751100
msgstr ""
10761101

1102+
#: shared-module/lvfontio/BuiltinFont.c
1103+
msgid "Font file not found"
1104+
msgstr ""
1105+
10771106
#: shared-bindings/bitmaptools/__init__.c
10781107
msgid "For L8 colorspace, input bitmap must have 8 bits per pixel"
10791108
msgstr ""
@@ -1409,6 +1438,10 @@ msgstr ""
14091438
msgid "Missing jmp_pin. %q[%u] jumps on pin"
14101439
msgstr ""
14111440

1441+
#: shared-module/lvfontio/BuiltinFont.c
1442+
msgid "Missing required font sections"
1443+
msgstr ""
1444+
14121445
#: shared-module/storage/__init__.c
14131446
msgid "Mount point directory missing"
14141447
msgstr ""
@@ -1975,6 +2008,7 @@ msgstr ""
19752008
#: shared-bindings/displayio/TileGrid.c
19762009
#: shared-bindings/memorymonitor/AllocationSize.c
19772010
#: shared-bindings/pulseio/PulseIn.c
2011+
#: shared-bindings/tilepalettemapper/TilePaletteMapper.c
19782012
msgid "Slices not supported"
19792013
msgstr ""
19802014

@@ -2042,7 +2076,9 @@ msgstr ""
20422076
msgid "Tile height must exactly divide bitmap height"
20432077
msgstr ""
20442078

2045-
#: shared-bindings/displayio/TileGrid.c shared-module/displayio/TileGrid.c
2079+
#: shared-bindings/displayio/TileGrid.c
2080+
#: shared-bindings/tilepalettemapper/TilePaletteMapper.c
2081+
#: shared-module/displayio/TileGrid.c
20462082
msgid "Tile index out of bounds"
20472083
msgstr ""
20482084

@@ -2268,6 +2304,10 @@ msgid ""
22682304
"declined or ignored."
22692305
msgstr ""
22702306

2307+
#: shared-module/lvfontio/BuiltinFont.c
2308+
msgid "Unsupported cmap format"
2309+
msgstr ""
2310+
22712311
#: shared-bindings/bitmaptools/__init__.c
22722312
msgid "Unsupported colorspace"
22732313
msgstr ""
@@ -3110,6 +3150,10 @@ msgstr ""
31103150
msgid "font must be 2048 bytes long"
31113151
msgstr ""
31123152

3153+
#: shared-bindings/terminalio/Terminal.c
3154+
msgid "font must be a BuiltinFont"
3155+
msgstr ""
3156+
31133157
#: extmod/moddeflate.c
31143158
msgid "format"
31153159
msgstr ""
@@ -4277,7 +4321,9 @@ msgstr ""
42774321
msgid "unreadable attribute"
42784322
msgstr ""
42794323

4280-
#: shared-bindings/displayio/TileGrid.c shared-bindings/vectorio/VectorShape.c
4324+
#: shared-bindings/displayio/TileGrid.c
4325+
#: shared-bindings/tilepalettemapper/TilePaletteMapper.c
4326+
#: shared-bindings/vectorio/VectorShape.c
42814327
msgid "unsupported %q type"
42824328
msgstr ""
42834329

ports/raspberrypi/boards/adafruit_fruit_jam/mpconfigboard.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@
3232

3333
#define CIRCUITPY_PSRAM_CHIP_SELECT (&pin_GPIO47)
3434

35-
// #define CIRCUITPY_CONSOLE_UART_TX (&pin_GPIO44)
36-
// #define CIRCUITPY_CONSOLE_UART_RX (&pin_GPIO45)
35+
#define CIRCUITPY_CONSOLE_UART_TX (&pin_GPIO44)
36+
#define CIRCUITPY_CONSOLE_UART_RX (&pin_GPIO45)
3737

3838
// #define CIRCUITPY_DEBUG_TINYUSB 0
3939

ports/raspberrypi/boards/adafruit_fruit_jam/mpconfigboard.mk

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,6 @@ EXTERNAL_FLASH_DEVICES = "W25Q128JVxQ"
1111

1212
# CIRCUITPY_DISPLAY_FONT = $(TOP)/tools/fonts/unifont-16.0.02-all.bdf
1313
# CIRCUITPY_FONT_EXTRA_CHARACTERS = "🖮🖱️"
14+
15+
# Enable the LVFONTIO module
16+
CIRCUITPY_LVFONTIO = 1

ports/raspberrypi/common-hal/picodvi/__init__.c

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,11 @@ static bool picodvi_autoconstruct_enabled(mp_int_t *default_width, mp_int_t *def
7474
if ((established_timings & 0x80) != 0 &&
7575
preferred_width % 1920 == 0 &&
7676
preferred_height % 1080 == 0) {
77-
*default_width = 720 / 2;
78-
*default_height = 400 / 2;
77+
*default_width = 720;
78+
*default_height = 400;
7979
} else {
80-
*default_width = 640 / 2;
81-
*default_height = 480 / 2;
80+
*default_width = 640;
81+
*default_height = 480;
8282
}
8383
}
8484
}
@@ -95,15 +95,15 @@ void picodvi_autoconstruct(void) {
9595
return;
9696
}
9797

98-
mp_int_t default_width = 320;
99-
mp_int_t default_height = 240;
98+
mp_int_t default_width = 640;
99+
mp_int_t default_height = 480;
100100
if (!picodvi_autoconstruct_enabled(&default_width, &default_height)) {
101101
return;
102102
}
103103

104104
mp_int_t width = default_width;
105105
mp_int_t height = 0;
106-
mp_int_t color_depth = 16;
106+
mp_int_t color_depth = 8;
107107
mp_int_t rotation = 0;
108108

109109
(void)common_hal_os_getenv_int("CIRCUITPY_DISPLAY_WIDTH", &width);
@@ -113,6 +113,9 @@ void picodvi_autoconstruct(void) {
113113

114114
if (height == 0) {
115115
switch (width) {
116+
case 720:
117+
height = 400;
118+
break;
116119
case 640:
117120
height = 480;
118121
break;
@@ -134,7 +137,7 @@ void picodvi_autoconstruct(void) {
134137
// invalid configuration, set back to default
135138
width = default_width;
136139
height = default_height;
137-
color_depth = 16;
140+
color_depth = 8;
138141
}
139142

140143
// construct framebuffer and display

ports/raspberrypi/supervisor/port.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,7 @@ void reset_to_bootloader(void) {
422422
}
423423

424424
void reset_cpu(void) {
425+
mp_hal_delay_ms(200);
425426
watchdog_reboot(0, SRAM_END, 0);
426427
watchdog_start_tick(12);
427428

py/circuitpy_defns.mk

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,9 @@ endif
396396
ifeq ($(CIRCUITPY_FONTIO),1)
397397
SRC_PATTERNS += fontio/%
398398
endif
399+
ifeq ($(CIRCUITPY_LVFONTIO),1)
400+
SRC_PATTERNS += lvfontio/%
401+
endif
399402
ifeq ($(CIRCUITPY_TILEPALETTEMAPPER),1)
400403
SRC_PATTERNS += tilepalettemapper/%
401404
endif
@@ -673,8 +676,10 @@ SRC_SHARED_MODULE_ALL = \
673676
epaperdisplay/__init__.c \
674677
epaperdisplay/EPaperDisplay.c \
675678
floppyio/__init__.c \
676-
fontio/BuiltinFont.c \
679+
fontio/OnDiskFont.c \
677680
fontio/__init__.c \
681+
lvfontio/OnDiskFont.c\
682+
lvfontio/__init__.c \
678683
fourwire/__init__.c \
679684
fourwire/FourWire.c \
680685
framebufferio/FramebufferDisplay.c \

py/circuitpy_mpconfig.mk

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -555,9 +555,12 @@ CFLAGS += -DCIRCUITPY_TERMINALIO=$(CIRCUITPY_TERMINALIO)
555555
CIRCUITPY_TERMINALIO_VT100 ?= $(CIRCUITPY_TERMINALIO)
556556
CFLAGS += -DCIRCUITPY_TERMINALIO_VT100=$(CIRCUITPY_TERMINALIO_VT100)
557557

558-
CIRCUITPY_FONTIO ?= $(call enable-if-all,$(CIRCUITPY_DISPLAYIO) $(CIRCUITPY_TERMINALIO))
558+
CIRCUITPY_FONTIO ?= $(CIRCUITPY_TERMINALIO)
559559
CFLAGS += -DCIRCUITPY_FONTIO=$(CIRCUITPY_FONTIO)
560560

561+
CIRCUITPY_LVFONTIO ?= $(CIRCUITPY_TERMINALIO)
562+
CFLAGS += -DCIRCUITPY_LVFONTIO=$(CIRCUITPY_LVFONTIO)
563+
561564
CIRCUITPY_TILEPALETTEMAPPER ?= $(CIRCUITPY_DISPLAYIO)
562565
CFLAGS += -DCIRCUITPY_TILEPALETTEMAPPER=$(CIRCUITPY_TILEPALETTEMAPPER)
563566

shared-bindings/displayio/TileGrid.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ void displayio_tilegrid_validate_pixel_shader(mp_obj_t pixel_shader) {
6262
//| convert the value and its location to a display native pixel color. This may be a simple color
6363
//| palette lookup, a gradient, a pattern or a color transformer.
6464
//|
65-
//| To save RAM usage, tile values are only allowed in the range from 0 to 255 inclusive (single byte values).
65+
//| When the total number of tiles is 256 or less, tile values are stored as single bytes (uint8_t).
66+
//| When the total number of tiles is more than 256, tile values are stored as double bytes (uint16_t).
6667
//|
6768
//| tile_width and tile_height match the height of the bitmap by default.
6869
//|
@@ -453,7 +454,7 @@ static mp_obj_t tilegrid_subscr(mp_obj_t self_in, mp_obj_t index_obj, mp_obj_t v
453454
return MP_OBJ_NULL; // op not supported
454455
} else {
455456
mp_int_t value = mp_obj_get_int(value_obj);
456-
mp_arg_validate_int_range(value, 0, 255, MP_QSTR_tile);
457+
mp_arg_validate_int_range(value, 0, self->tiles_in_bitmap - 1, MP_QSTR_tile);
457458

458459
common_hal_displayio_tilegrid_set_tile(self, x, y, value);
459460
}

shared-bindings/displayio/TileGrid.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ extern const mp_obj_type_t displayio_tilegrid_type;
1313
void common_hal_displayio_tilegrid_construct(displayio_tilegrid_t *self, mp_obj_t bitmap,
1414
uint16_t bitmap_width_in_tiles, uint16_t bitmap_height_in_tiles,
1515
mp_obj_t pixel_shader, uint16_t width, uint16_t height,
16-
uint16_t tile_width, uint16_t tile_height, uint16_t x, uint16_t y, uint8_t default_tile);
16+
uint16_t tile_width, uint16_t tile_height, uint16_t x, uint16_t y, uint16_t default_tile);
1717

1818
bool common_hal_displayio_tilegrid_get_hidden(displayio_tilegrid_t *self);
1919
void common_hal_displayio_tilegrid_set_hidden(displayio_tilegrid_t *self, bool hidden);
@@ -43,9 +43,9 @@ uint16_t common_hal_displayio_tilegrid_get_height(displayio_tilegrid_t *self);
4343
uint16_t common_hal_displayio_tilegrid_get_tile_width(displayio_tilegrid_t *self);
4444
uint16_t common_hal_displayio_tilegrid_get_tile_height(displayio_tilegrid_t *self);
4545

46-
uint8_t common_hal_displayio_tilegrid_get_tile(displayio_tilegrid_t *self, uint16_t x, uint16_t y);
47-
void common_hal_displayio_tilegrid_set_tile(displayio_tilegrid_t *self, uint16_t x, uint16_t y, uint8_t tile_index);
46+
uint16_t common_hal_displayio_tilegrid_get_tile(displayio_tilegrid_t *self, uint16_t x, uint16_t y);
47+
void common_hal_displayio_tilegrid_set_tile(displayio_tilegrid_t *self, uint16_t x, uint16_t y, uint16_t tile_index);
4848

4949
// Private API for scrolling the TileGrid.
5050
void common_hal_displayio_tilegrid_set_top_left(displayio_tilegrid_t *self, uint16_t x, uint16_t y);
51-
void common_hal_displayio_tilegrid_set_all_tiles(displayio_tilegrid_t *self, uint8_t tile_index);
51+
void common_hal_displayio_tilegrid_set_all_tiles(displayio_tilegrid_t *self, uint16_t tile_index);
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
//
3+
// SPDX-FileCopyrightText: Copyright (c) 2024 Scott Shawcroft for Adafruit Industries
4+
//
5+
// SPDX-License-Identifier: MIT
6+
7+
#include "shared-bindings/lvfontio/OnDiskFont.h"
8+
9+
#include <stdint.h>
10+
11+
#include "shared/runtime/context_manager_helpers.h"
12+
#include "py/binary.h"
13+
#include "py/objproperty.h"
14+
#include "py/objstr.h"
15+
#include "py/runtime.h"
16+
#include "shared-bindings/microcontroller/Pin.h"
17+
#include "shared-bindings/util.h"
18+
19+
//| from typing_extensions import Protocol # for compat with python < 3.8
20+
//|
21+
//|
22+
//| class FontProtocol(Protocol):
23+
//| """A protocol shared by `OnDiskFont`"""
24+
//|
25+
//| def get_bounding_box(self) -> Union[Tuple[int, int], Tuple[int, int, int, int]]:
26+
//| """Retrieve the maximum bounding box of any glyph in the font.
27+
//|
28+
//| The four element version is ``(width, height, x_offset, y_offset)``.
29+
//| The two element version is ``(width, height)``, in which
30+
//| ``x_offset`` and ``y_offset`` are assumed to be zero."""
31+
//| pass
32+
//|
33+
//|
34+
35+
//| class OnDiskFont:
36+
//| """A font built into CircuitPython for use with LVGL"""
37+
//|
38+
//| def __init__(self, file_path: str, max_glyphs: int = 100) -> None:
39+
//| """Create a OnDiskFont by loading an LVGL font file from the filesystem.
40+
//|
41+
//| :param str file_path: The path to the font file
42+
//| :param int max_glyphs: Maximum number of glyphs to cache at once
43+
//| """
44+
//| ...
45+
//|
46+
47+
//| bitmap: displayio.Bitmap
48+
//| """Bitmap containing all font glyphs starting with ASCII and followed by unicode. This is useful for use with LVGL."""
49+
//|
50+
static mp_obj_t lvfontio_builtinfont_obj_get_bitmap(mp_obj_t self_in) {
51+
lvfontio_ondiskfont_t *self = MP_OBJ_TO_PTR(self_in);
52+
return common_hal_lvfontio_ondiskfont_get_bitmap(self);
53+
}
54+
MP_DEFINE_CONST_FUN_OBJ_1(lvfontio_builtinfont_get_bitmap_obj, lvfontio_builtinfont_obj_get_bitmap);
55+
56+
MP_PROPERTY_GETTER(lvfontio_builtinfont_bitmap_obj,
57+
(mp_obj_t)&lvfontio_builtinfont_get_bitmap_obj);
58+
59+
//| def get_bounding_box(self) -> Tuple[int, int]:
60+
//| """Returns the maximum bounds of all glyphs in the font in a tuple of two values: width, height."""
61+
//| ...
62+
//|
63+
//|
64+
static mp_obj_t lvfontio_builtinfont_obj_get_bounding_box(mp_obj_t self_in) {
65+
lvfontio_ondiskfont_t *self = MP_OBJ_TO_PTR(self_in);
66+
67+
return common_hal_lvfontio_ondiskfont_get_bounding_box(self);
68+
}
69+
MP_DEFINE_CONST_FUN_OBJ_1(lvfontio_builtinfont_get_bounding_box_obj, lvfontio_builtinfont_obj_get_bounding_box);
70+
71+
static const mp_rom_map_elem_t lvfontio_builtinfont_locals_dict_table[] = {
72+
{ MP_ROM_QSTR(MP_QSTR_bitmap), MP_ROM_PTR(&lvfontio_builtinfont_bitmap_obj) },
73+
{ MP_ROM_QSTR(MP_QSTR_get_bounding_box), MP_ROM_PTR(&lvfontio_builtinfont_get_bounding_box_obj) },
74+
};
75+
static MP_DEFINE_CONST_DICT(lvfontio_builtinfont_locals_dict, lvfontio_builtinfont_locals_dict_table);
76+
77+
static mp_obj_t lvfontio_builtinfont_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
78+
enum { ARG_file_path, ARG_max_glyphs };
79+
static const mp_arg_t allowed_args[] = {
80+
{ MP_QSTR_file_path, MP_ARG_OBJ | MP_ARG_REQUIRED },
81+
{ MP_QSTR_max_glyphs, MP_ARG_INT, {.u_int = 100} },
82+
};
83+
84+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
85+
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
86+
87+
// Allocate the BuiltinFont object
88+
lvfontio_ondiskfont_t *self = m_new_obj(lvfontio_ondiskfont_t);
89+
self->base.type = &lvfontio_ondiskfont_type;
90+
91+
// Extract arguments
92+
mp_obj_t file_path_obj = args[ARG_file_path].u_obj;
93+
mp_uint_t max_glyphs = args[ARG_max_glyphs].u_int;
94+
95+
// Get the C string from the Python string
96+
const char *file_path = mp_obj_str_get_str(file_path_obj);
97+
98+
// Always use GC allocator for Python-created objects
99+
common_hal_lvfontio_ondiskfont_construct(self, file_path, max_glyphs, true);
100+
101+
return MP_OBJ_FROM_PTR(self);
102+
}
103+
104+
MP_DEFINE_CONST_OBJ_TYPE(
105+
lvfontio_ondiskfont_type,
106+
MP_QSTR_OnDiskFont,
107+
MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS,
108+
make_new, lvfontio_builtinfont_make_new,
109+
locals_dict, &lvfontio_builtinfont_locals_dict
110+
);

0 commit comments

Comments
 (0)