master
Ondřej Hruška 2 years ago
commit b4f9b87ce0
  1. 3
      .gitignore
  2. 20
      CMakeLists.txt
  3. 276
      main.c
  4. 73
      pico_sdk_import.cmake
  5. 4
      ufb/.gitignore
  6. 17
      ufb/Makefile
  7. 72
      ufb/fb_7seg.c
  8. 32
      ufb/fb_7seg.h
  9. 96
      ufb/fb_text.c
  10. 29
      ufb/fb_text.h
  11. 81
      ufb/font.c
  12. 35
      ufb/font.h
  13. 102
      ufb/font_35.inc.c
  14. 102
      ufb/font_45.inc.c
  15. 116
      ufb/font_57.inc.c
  16. 598
      ufb/fontedit_35.c
  17. 598
      ufb/fontedit_45.c
  18. 919
      ufb/fontedit_57.c
  19. 14
      ufb/fontedit_gen_table.c
  20. 48
      ufb/fontedit_render.inc.c
  21. 282
      ufb/framebuffer.c
  22. 87
      ufb/framebuffer.h
  23. 8
      ufb/framebuffer_config.h
  24. 164
      ufb/main.c
  25. 18
      ufb/progmem.h
  26. 129
      ufb/utf8.c
  27. 93
      ufb/utf8.h

3
.gitignore vendored

@ -0,0 +1,3 @@
build
cmake-build-*
.idea/

@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(test_project C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()
add_executable(test
main.c
ufb/framebuffer.c ufb/utf8.c ufb/font.c ufb/fb_7seg.c ufb/fb_text.c
)
target_include_directories(test PRIVATE ufb)
pico_enable_stdio_usb(test 1)
#pico_enable_stdio_uart(test 1)
pico_add_extra_outputs(test)
target_link_libraries(test pico_stdlib hardware_spi)

276
main.c

@ -0,0 +1,276 @@
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "pico/binary_info.h"
#include "hardware/spi.h"
#include "framebuffer.h"
#include "fb_text.h"
const uint LED_PIN = 25;
const uint DC_PIN = 20;
const uint RST_PIN = 21;
#define SSD1309_HEIGHT 64
/* Picotool info */
// Make the SPI pins available to picotool
bi_decl(bi_2pins_with_func(PICO_DEFAULT_SPI_TX_PIN, PICO_DEFAULT_SPI_SCK_PIN, GPIO_FUNC_SPI))
bi_decl(bi_1pin_with_name(PICO_DEFAULT_SPI_CSN_PIN, "SPI CS"))
bi_decl(bi_1pin_with_name(DC_PIN, "OLED DC"))
bi_decl(bi_1pin_with_name(RST_PIN, "OLED RST"))
bi_decl(bi_1pin_with_name(LED_PIN, "Blink LED"))
static inline void cs_select() {
asm volatile("nop \n nop \n nop");
gpio_put(PICO_DEFAULT_SPI_CSN_PIN, 0); // Active low
asm volatile("nop \n nop \n nop");
}
static inline void cs_deselect() {
asm volatile("nop \n nop \n nop");
gpio_put(PICO_DEFAULT_SPI_CSN_PIN, 1);
asm volatile("nop \n nop \n nop");
}
static inline void dc_command() {
gpio_put(DC_PIN, 0);
}
static inline void dc_data() {
gpio_put(DC_PIN, 1);
}
static void oled_command16(uint8_t cmd, uint8_t arg) {
uint8_t buf[2];
buf[0] = cmd;
buf[1] = arg;
dc_command();
cs_select();
spi_write_blocking(spi_default, buf, 2);
cs_deselect();
}
static void oled_command(uint8_t cmd) {
dc_command();
cs_select();
spi_write_blocking(spi_default, &cmd, 1);
cs_deselect();
}
static void oled_reset() {
// Issue a display reset
gpio_put(RST_PIN, 0);
sleep_ms(1);
gpio_put(RST_PIN, 1);
sleep_ms(1);
}
int main() {
bi_decl(bi_program_description("This is a test binary."));
bi_decl(bi_1pin_with_name(LED_PIN, "On-board LED"));
stdio_init_all();
gpio_init(LED_PIN);
gpio_set_dir(LED_PIN, GPIO_OUT);
// This example will use SPI0 at 10MHz.
spi_init(spi_default, 10 * 1000 * 1000);
gpio_set_function(PICO_DEFAULT_SPI_SCK_PIN, GPIO_FUNC_SPI);
gpio_set_function(PICO_DEFAULT_SPI_TX_PIN, GPIO_FUNC_SPI);
// Chip select is active-low, so we'll initialise it to a driven-high state
gpio_init(PICO_DEFAULT_SPI_CSN_PIN);
gpio_set_dir(PICO_DEFAULT_SPI_CSN_PIN, GPIO_OUT);
gpio_put(PICO_DEFAULT_SPI_CSN_PIN, 1);
gpio_init(DC_PIN);
gpio_set_dir(DC_PIN, GPIO_OUT);
gpio_init(RST_PIN);
gpio_set_dir(RST_PIN, GPIO_OUT);
oled_reset();
// simplified display init
oled_command(0xAE); /* Display off */
//oled_command16(0xD5, 0xF0); // set clock speed
oled_command16(0x20, 0b00); // page addressing mode: 00 - horizontal, 01 vertical, 10 page
oled_command16(0x81, 0xFF);
//oled_command16(0xD9, 0xff);
oled_command(0xAF); /*--turn on SSD1309 panel */
#if 0
/* Init OLED */
oled_command(0xAE); /* Display off */
// Configure the display - only setting things that differ from the default:
oled_command16(0x20, 0b10); /* Set Memory Addressing Mode */
/* 00,Horizontal Addressing Mode; 01,Vertical Addressing Mode; */
/* 10,Page Addressing Mode (RESET); 11,Invalid */
oled_command(0xB0); /*Set Page Start Address for Page Addressing Mode, B0-B7 */
#ifdef SSD1309_MIRROR_VERT
oled_command(0xC1);
#else
oled_command(0xC0);
#endif
// lower nibble is value
oled_command(0x00); /*---set low column address 00~0F */
oled_command(0x10); /*---set high column address 10~1F */
oled_command(0x40); /*--set start line address - 40~7F */
oled_command16(0x81, 0x7F); /*--set contrast control register - ? 0xFF */
#ifdef SSD1309_MIRROR_HORIZ
oled_command(0xA1);
#else
oled_command(0xA0);
#endif
#ifdef SSD1309_INVERSE_COLOR
oled_command(0xA7); /*--set inverse color */
#else
oled_command(0xA6); /*--set normal color */
#endif
/*--set multiplex ratio(1 to 64) */
#if (SSD1309_HEIGHT == 32)
oled_command16(0xA8, 0x1F);
#elif (SSD1309_HEIGHT == 64)
oled_command16(0xA8, 0x3F);
#else
#error "bad height"
#endif
oled_command(0xA4); /* 0xA4, Output follows RAM content;0xa5,Output ignores RAM content */
oled_command16(0xD3, 0x00); /*-set display offset */
oled_command16(0xD5, 0xF0); /*--set display clock divide ratio/oscillator frequency */
oled_command16(0xD9, 0x22); /*--set pre-charge period */
/*--set com pins hardware configuration */
#if (SSD1309_HEIGHT == 32)
oled_command16(0xDA, 0x02);
#elif (SSD1309_HEIGHT == 64)
oled_command16(0xDA, 0x12);
#else
#error "bad height"
#endif
oled_command16(0xDB, 0x34); /*--set vcomh */
// ??? not listed in the datasheet
// oled_command(0x8D); /*--set DC-DC enable */
// oled_command(0x14); /* */
oled_command(0xAF); /*--turn on SSD1309 panel */
oled_command(0xA5); // entire display on - for test
#endif
#define ZIR_W 12
#define ZIR_H 24
const uint8_t zirafa[(ZIR_H/8) * ZIR_W] = {
// levo
0b10000000,
0b01000000,
0b00100000,
0b00010000,
0b00001000,
0b00000100,
0b00000010,
0b00011001,
0b00011111,
0b11111000,
0b00011111,
0b00001000,
//
0b10000000,
0b01000000,
0b00100000,
0b00010000,
0b00001000,
0b00000100,
0b00000010,
0b00000001,
0b10000000,
0b11111111,
0b00100000,
0b00010000,
//
0b00110000,
0b10001000,
0b11111000,
0b00011000,
0b11111000,
0b10011000,
0b00011000,
0b10011000,
0b11111000,
0b00011111,
0b11111000,
0b10000000,
};
fb_clear();
fb_bitmap(5, 5, ZIR_W, ZIR_H, zirafa, 1);
fb_text(40, 23, "MEOW", FONT_DOUBLE, 1);
fb_text(50, 35, "meow", FONT_DOUBLE, 1);
fb_frame(0, 0, 128, 64, 2, 1);
fb_blit();
// dc_data();
// cs_select();
// uint8_t pattern = 0;
// for (uint i = 0; i < 128 * 8; i++) {
// pattern = i & 0xFF;
// spi_write_blocking(spi_default, &pattern, 1);
// }
// cs_deselect();
while (1) {
oled_command(0xA6); // invert
gpio_put(LED_PIN, 0);
sleep_ms(1000);
oled_command(0xA7); // uninvert
gpio_put(LED_PIN, 1);
//puts("Hello World\n");
sleep_ms(1000);
}
}
void fb_blit() {
dc_data();
cs_select();
spi_write_blocking(spi_default, fb, FB_LEN);
cs_deselect();
}

@ -0,0 +1,73 @@
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
# This can be dropped into an external project to help locate this SDK
# It should be include()ed prior to project()
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
endif ()
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
if (NOT PICO_SDK_PATH)
if (PICO_SDK_FETCH_FROM_GIT)
include(FetchContent)
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
if (PICO_SDK_FETCH_FROM_GIT_PATH)
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
endif ()
# GIT_SUBMODULES_RECURSE was added in 3.17
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0")
FetchContent_Declare(
pico_sdk
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG master
GIT_SUBMODULES_RECURSE FALSE
)
else ()
FetchContent_Declare(
pico_sdk
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG master
)
endif ()
if (NOT pico_sdk)
message("Downloading Raspberry Pi Pico SDK")
FetchContent_Populate(pico_sdk)
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
endif ()
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
else ()
message(FATAL_ERROR
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
)
endif ()
endif ()
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
if (NOT EXISTS ${PICO_SDK_PATH})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
endif ()
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
endif ()
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
include(${PICO_SDK_INIT_CMAKE_FILE})

4
ufb/.gitignore vendored

@ -0,0 +1,4 @@
*.o
ufb-test
.idea/

@ -0,0 +1,17 @@
.PHONY: build run fonts
all: fonts ufb-test
fonts: fontedit_35.c fontedit_45.c fontedit_57.c
tcc -run fontedit_35.c > font_35.inc.c || true
tcc -run fontedit_45.c > font_45.inc.c || true
tcc -run fontedit_57.c > font_57.inc.c || true
run: ufb-test
./ufb-test
clean:
rm -f ufb-test
ufb-test: main.c framebuffer.c framebuffer.h framebuffer_config.h utf8.c utf8.h progmem.h font.c font.h fb_7seg.c fb_7seg.h fb_text.c fb_text.h
$(CC) -o $@ $^ -I.

@ -0,0 +1,72 @@
#include "fb_7seg.h"
#include "progmem.h"
enum SevenSegBars {
T = 1, RT = 2, RB = 4, B = 8, LB = 16, LT = 32, M = 64
};
static const uint8_t PROGMEM seven[] = {
[0] = T | RT | RB | B | LB | LT,
[1] = RT | RB,
[2] = T | RT | M | LB | B,
[3] = T | RT | M | RB | B,
[4] = RT | RB | M | LT,
[5] = T | LT | M | RB | B,
[6] = T | LT | LB | B | RB | M,
[7] = T | RT | RB,
[8] = T | LT | RT | LB | RB | B | M,
[9] = T | LT | RT | RB | B | M,
};
fbpos_t fb_7seg_dig(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbpos_t th, uint8_t digit, fbcolor_t color)
{
const uint8_t mask = digit > 9 ? 0 : pgm_read_byte(&seven[digit]);
const fbpos_t wi = w - th * 2;
const fbpos_t hi = (h - th * 3) / 2;
bool bcolor = !color; // changed for XOR
fb_rect(x + th,
y,
wi,
th, bcolor ^ (bool) (mask & T));
fb_rect(x + th,
y + th + hi,
wi,
th, bcolor ^ (bool) (mask & M));
fb_rect(x + th,
y + th * 2 + hi * 2,
wi,
th, bcolor ^ (bool) (mask & B));
fb_rect(x,
y + th,
th,
hi, bcolor ^ (bool) (mask & LT));
fb_rect(x + th + wi,
y + hi + th,
th,
hi, bcolor ^ (bool) (mask & LB));
fb_rect(x + th + wi,
y + th,
th,
hi, bcolor ^ (bool) (mask & RT));
fb_rect(x + th + wi,
y + th * 2 + hi,
th,
hi, bcolor ^ (bool) (mask & RB));
return w;
}
fbpos_t fb_7seg_period(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbpos_t th, fbcolor_t color)
{
const fbpos_t hi = (h - th * 3) / 2;
fb_rect(x, y + hi * 2 + th * 2, th, th, color);
return th;
}

@ -0,0 +1,32 @@
/**
* Draw 7-seg digits to the framebuffer
*/
#ifndef FB_7SEG_H
#define FB_7SEG_H
#include "framebuffer.h"
/// Draw a 7-segment digit. Returns its width (without spacing)
///
/// \param x - pos X (left top)
/// \param y - pos Y (left top)
/// \param w - full digit width
/// \param h - full digit height; will be adjusted down if needed
/// \param th - thickness
/// \param digit - digit 0-9
/// \return width taken
fbpos_t fb_7seg_dig(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbpos_t th, uint8_t digit, fbcolor_t color);
/// Draw a 7-segment period. Returns its width (without spacing).
/// Digit height is (w * 2 - th)
///
/// \param x - pos X (digit left top)
/// \param y - pos Y (digit left top)
/// \param w - full digit width
/// \param h - full digit height; will be adjusted down if needed
/// \param th - thickness
/// \return width taken
fbpos_t fb_7seg_period(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbpos_t th, fbcolor_t color);
#endif //FB_7SEG_H

@ -0,0 +1,96 @@
/**
* TODO file description
*/
#include "fb_text.h"
#include "framebuffer.h"
#include "utf8.h"
#include "font.h"
void fb_text_P_or_RAM(fbpos_t x, fbpos_t y, const char *text, uint8_t flags, fbcolor_t color, bool is_pgmem) {
struct Utf8Iterator iter;
Utf8Iterator_Init(&iter, text);
iter.is_progmem = is_pgmem;
struct Utf8Char uchar;
uint8_t symw;
uint8_t symh;
if (flags & FONT_4X5) {
symw = 4;
symh = 5;
} else if (flags & FONT_3X5) {
symw = 3;
symh = 5;
} else {
symw = 5;
symh = 7;
}
while ((uchar = Utf8Iterator_Next(&iter)).uint) {
const uint8_t *data;
if (flags & FONT_4X5) {
const font4x_bitmap_t *sym = font45_getsym(&uchar);
data = sym->data;
} else if (flags & FONT_3X5) {
const font3x_bitmap_t *sym = font35_getsym(&uchar);
data = sym->data;
} else{
const font5x_bitmap_t *sym = font57_getsym(&uchar);
data = sym->data;
}
if (0 == (flags & FONT_DOUBLE)) {
// no double, using normal format
#if IS_AVR
fb_bitmap_P(x, y, symw, symh, data, color);
#else
fb_bitmap(x, y, symw, symh, data, color);
#endif
if (flags & FONT_BOLD) {
x++;
#if IS_AVR
fb_bitmap_P(x, y, symw, symh, data, color);
#else
fb_bitmap(x, y, symw, symh, data, color);
#endif
x += symw+2;
} else {
x += symw+1;
}
} else {
// slow, pixel-by-pixel drawing
const uint8_t pxw = (flags & FONT_DOUBLEW) ? 2 : 1;
const uint8_t pxh = (flags & FONT_DOUBLEH) ? 2 : 1;
for(fbpos_t xx = 0; xx < symw; xx++) {
uint8_t column = pgm_read_byte(&data[xx]);
for(fbpos_t yy = 0; yy < symh; yy++) {
if (column & (1 << yy)) {
fb_rect(x + xx * pxw, y + yy * pxh, pxw, pxh, color);
if (flags & FONT_BOLD) {
fb_rect(x + (xx + 1) * pxw, y + yy * pxh, pxw, pxh, color);
}
}
}
}
if (flags & FONT_BOLD) {
x += (symw+2) * pxw;
} else {
x += (symw+1) * pxw;
}
}
}
}
void fb_text_P(fbpos_t x, fbpos_t y, const char *text, uint8_t flags, fbcolor_t color)
{
fb_text_P_or_RAM(x, y, text, flags, color, true);
}
void fb_text(fbpos_t x, fbpos_t y, const char *text, uint8_t flags, fbcolor_t color) {
fb_text_P_or_RAM(x, y, text, flags, color, false);
}

@ -0,0 +1,29 @@
/**
* Draw text
*/
#ifndef UFB_FB_TEXT_H
#define UFB_FB_TEXT_H
#include "framebuffer.h"
/// Use tiny font 5x4, supports only digits and select symbols
#define FONT_4X5 8
#define FONT_3X5 16
#define FONT_5X7 0 // default font
/// Use bold variant (each character is printed twice, 1px offset)
#define FONT_BOLD 1
/// Print characters 2x wider
#define FONT_DOUBLEW 2
/// Print characters 2x taller
#define FONT_DOUBLEH 4
/// Print characters 2x wider and taller
#define FONT_DOUBLE (FONT_DOUBLEW | FONT_DOUBLEH)
/// Print text stored in flash
void fb_text_P(fbpos_t x, fbpos_t y, const char *text, uint8_t flags, fbcolor_t color);
/// Print text stored in RAM
void fb_text(fbpos_t x, fbpos_t y, const char *text, uint8_t flags, fbcolor_t color);
#endif //UFB_FB_TEXT_H

@ -0,0 +1,81 @@
#include "font.h"
#include "utf8.h"
#include "progmem.h"
#include <stdint.h>
union utf_lookup {
const char symbol[4];
/// symbol as uint, but not decoded - just for matching!
const uint32_t uint;
};
struct utf_glyph5x {
union utf_lookup utf;
font5x_bitmap_t graphic;
};
struct utf_glyph4x {
union utf_lookup utf;
font4x_bitmap_t graphic;
};
struct utf_glyph3x {
union utf_lookup utf;
font3x_bitmap_t graphic;
};
#include "font_35.inc.c"
#include "font_45.inc.c"
#include "font_57.inc.c"
#define ASCII_START 0x20
#define ASCII_END 0x7e
#define FONT_GETSYM_IMPL(rettype, symtype, fontname) \
const rettype *fontname##_getsym(const struct Utf8Char *ch) { \
const uint8_t byte0 = ch->bytes[0]; \
if (byte0 < ASCII_START) { \
goto fail; \
} \
if (byte0 <= ASCII_END) { \
return &fontname##_ascii[byte0 - ASCII_START]; \
} \
for (uint8_t i = 0; i < sizeof(fontname##_extra) / sizeof(fontname##_extra[0]); i++) { \
const struct symtype *sym = &fontname##_extra[i]; \
const uint32_t table_ch = pgm_read_dword(&sym->utf.uint); \
if (table_ch == ch->uint) { \
return &sym->graphic; \
} \
} \
fail: return &fontname ## _extra[0].graphic; \
}
FONT_GETSYM_IMPL(font5x_bitmap_t, utf_glyph5x, font57)
FONT_GETSYM_IMPL(font4x_bitmap_t, utf_glyph4x, font45)
FONT_GETSYM_IMPL(font3x_bitmap_t, utf_glyph3x, font35)
#if 0
const font5x_bitmap_t *font57_getsym(const struct Utf8Char *ch)
{
const uint8_t byte0 = ch->bytes[0];
if (byte0 < ASCII_START) {
goto fail;
}
if (byte0 <= ASCII_END) {
return &font57_ascii[byte0 - ASCII_START];
}
for (uint8_t i = 0; i < sizeof(font57_extra) / sizeof(font57_extra[0]); i++) {
const struct utf_glyph5x *sym = &font57_extra[i];
const uint32_t table_ch = pgm_read_dword(&sym->uint);
if (table_ch == ch->uint) {
return &sym->graphic;
}
}
fail:
return &font57_extra[0].graphic; // replacement character
}
#endif

@ -0,0 +1,35 @@
/**
* UTF-8 capable bitmap font
*
* Created on 2020/01/04.
*/
#ifndef GFX_FONT_H
#define GFX_FONT_H
#include "font.h"
#include "utf8.h"
#include <stdint.h>
typedef struct {
uint8_t data[5];
} font5x_bitmap_t;
typedef struct {
uint8_t data[4];
} font4x_bitmap_t;
typedef struct {
uint8_t data[3];
} font3x_bitmap_t;
/// Get font graphic for a character.
///
/// The returned pointer is PROGMEM!
const font5x_bitmap_t *font57_getsym(const struct Utf8Char *ch);
const font4x_bitmap_t *font45_getsym(const struct Utf8Char *ch);
const font3x_bitmap_t *font35_getsym(const struct Utf8Char *ch);
#endif //GFX_FONT_H

@ -0,0 +1,102 @@
static const font3x_bitmap_t PROGMEM font35_ascii[] = {
{{0x00, 0x00, 0x00}},
{{0x00, 0x17, 0x00}},
{{0x03, 0x00, 0x03}},
{{0x0a, 0x1f, 0x0a}},
{{0x16, 0x1f, 0x1a}},
{{0x19, 0x04, 0x13}},
{{0x1d, 0x17, 0x19}},
{{0x00, 0x03, 0x00}},
{{0x0e, 0x11, 0x00}},
{{0x11, 0x0e, 0x00}},
{{0x05, 0x02, 0x05}},
{{0x04, 0x0e, 0x04}},
{{0x10, 0x08, 0x00}},
{{0x04, 0x04, 0x04}},
{{0x18, 0x18, 0x00}},
{{0x18, 0x04, 0x03}},
{{0x1f, 0x11, 0x1f}},
{{0x12, 0x1f, 0x10}},
{{0x1d, 0x15, 0x17}},
{{0x11, 0x15, 0x1f}},
{{0x07, 0x04, 0x1f}},
{{0x17, 0x15, 0x1d}},
{{0x1f, 0x15, 0x1d}},
{{0x03, 0x01, 0x1f}},
{{0x1f, 0x15, 0x1f}},
{{0x17, 0x15, 0x1f}},
{{0x00, 0x0a, 0x00}},
{{0x10, 0x0a, 0x00}},
{{0x04, 0x0a, 0x11}},
{{0x0a, 0x0a, 0x0a}},
{{0x11, 0x0a, 0x04}},
{{0x01, 0x15, 0x07}},
{{0x0f, 0x11, 0x17}},
{{0x1f, 0x05, 0x1f}},
{{0x1f, 0x15, 0x1e}},
{{0x1f, 0x11, 0x11}},
{{0x1f, 0x11, 0x0e}},
{{0x1f, 0x15, 0x11}},
{{0x1f, 0x05, 0x01}},
{{0x1f, 0x11, 0x1d}},
{{0x1f, 0x04, 0x1f}},
{{0x11, 0x1f, 0x11}},
{{0x19, 0x11, 0x1f}},
{{0x1f, 0x04, 0x1b}},
{{0x1f, 0x10, 0x10}},
{{0x1f, 0x03, 0x1f}},
{{0x1f, 0x01, 0x1e}},
{{0x1f, 0x11, 0x1f}},
{{0x1f, 0x05, 0x07}},
{{0x0f, 0x09, 0x1f}},
{{0x1f, 0x0d, 0x17}},
{{0x17, 0x15, 0x1d}},
{{0x01, 0x1f, 0x01}},
{{0x1f, 0x10, 0x1f}},
{{0x0f, 0x10, 0x0f}},
{{0x1f, 0x18, 0x1f}},
{{0x1b, 0x04, 0x1b}},
{{0x07, 0x1c, 0x07}},
{{0x19, 0x15, 0x13}},
{{0x00, 0x1f, 0x11}},
{{0x03, 0x04, 0x18}},
{{0x11, 0x1f, 0x00}},
{{0x02, 0x01, 0x02}},
{{0x10, 0x10, 0x10}},
{{0x01, 0x02, 0x00}},
{{0x1d, 0x15, 0x1e}},
{{0x1f, 0x14, 0x1c}},
{{0x1c, 0x14, 0x14}},
{{0x1c, 0x14, 0x1f}},
{{0x0f, 0x15, 0x17}},
{{0x04, 0x1f, 0x05}},
{{0x17, 0x15, 0x0f}},
{{0x1f, 0x04, 0x1c}},
{{0x14, 0x1d, 0x10}},
{{0x18, 0x10, 0x1d}},
{{0x1f, 0x08, 0x14}},
{{0x11, 0x1f, 0x10}},
{{0x1c, 0x0c, 0x1c}},
{{0x1c, 0x04, 0x18}},
{{0x1c, 0x14, 0x1c}},
{{0x1e, 0x0a, 0x0e}},
{{0x0e, 0x0a, 0x1e}},
{{0x1c, 0x04, 0x04}},
{{0x16, 0x16, 0x1a}},
{{0x02, 0x1f, 0x12}},
{{0x1e, 0x10, 0x1e}},
{{0x0e, 0x10, 0x0e}},
{{0x1c, 0x18, 0x1c}},
{{0x14, 0x08, 0x14}},
{{0x16, 0x14, 0x1e}},
{{0x1a, 0x16, 0x16}},
{{0x04, 0x0e, 0x11}},
{{0x00, 0x1f, 0x00}},
{{0x11, 0x0e, 0x04}},
{{0x04, 0x02, 0x06}},
};
static const struct utf_glyph3x PROGMEM font35_extra[] = {
{.utf={.symbol="<EFBFBD>"}, {{0x1f, 0x1f, 0x1f}}},
{.utf={.symbol="°"}, {{0x07, 0x05, 0x07}}},
};

@ -0,0 +1,102 @@
static const font4x_bitmap_t PROGMEM font45_ascii[] = {
{{0x00, 0x00, 0x00, 0x00}},
{{0x00, 0x17, 0x17, 0x00}},
{{0x03, 0x00, 0x03, 0x00}},
{{0x0a, 0x1f, 0x0a, 0x1f}},
{{0x12, 0x1d, 0x17, 0x09}},
{{0x12, 0x08, 0x04, 0x12}},
{{0x1f, 0x15, 0x1f, 0x14}},
{{0x00, 0x03, 0x00, 0x00}},
{{0x00, 0x0e, 0x11, 0x00}},
{{0x00, 0x11, 0x0e, 0x00}},
{{0x05, 0x02, 0x02, 0x05}},
{{0x04, 0x0e, 0x04, 0x00}},
{{0x00, 0x10, 0x08, 0x00}},
{{0x00, 0x04, 0x04, 0x04}},
{{0x00, 0x18, 0x18, 0x00}},
{{0x10, 0x08, 0x04, 0x02}},
{{0x0e, 0x11, 0x11, 0x0e}},
{{0x04, 0x12, 0x1f, 0x10}},
{{0x12, 0x19, 0x15, 0x12}},
{{0x0a, 0x11, 0x15, 0x0a}},
{{0x07, 0x04, 0x1e, 0x04}},
{{0x17, 0x15, 0x15, 0x09}},
{{0x0e, 0x15, 0x15, 0x09}},
{{0x11, 0x09, 0x05, 0x03}},
{{0x0a, 0x15, 0x15, 0x0a}},
{{0x12, 0x15, 0x15, 0x0a}},
{{0x00, 0x00, 0x0a, 0x00}},
{{0x00, 0x10, 0x0a, 0x00}},
{{0x04, 0x0a, 0x11, 0x00}},
{{0x0a, 0x0a, 0x0a, 0x00}},
{{0x11, 0x0a, 0x04, 0x00}},
{{0x01, 0x01, 0x15, 0x02}},
{{0x1f, 0x11, 0x17, 0x16}},
{{0x1e, 0x05, 0x05, 0x1e}},
{{0x1f, 0x15, 0x15, 0x0a}},
{{0x0e, 0x11, 0x11, 0x0a}},
{{0x1f, 0x11, 0x11, 0x0e}},
{{0x1f, 0x15, 0x15, 0x11}},
{{0x1f, 0x05, 0x05, 0x01}},
{{0x0e, 0x11, 0x15, 0x0d}},
{{0x1f, 0x04, 0x04, 0x1f}},
{{0x00, 0x11, 0x1f, 0x11}},
{{0x09, 0x11, 0x11, 0x0f}},
{{0x1f, 0x04, 0x0a, 0x11}},
{{0x1f, 0x10, 0x10, 0x10}},
{{0x1f, 0x03, 0x01, 0x1f}},
{{0x1f, 0x02, 0x04, 0x1f}},
{{0x0e, 0x11, 0x11, 0x0e}},
{{0x1f, 0x05, 0x05, 0x02}},
{{0x0e, 0x11, 0x19, 0x1e}},
{{0x1f, 0x05, 0x0d, 0x12}},
{{0x12, 0x15, 0x15, 0x09}},
{{0x01, 0x1f, 0x01, 0x00}},
{{0x0f, 0x10, 0x10, 0x0f}},
{{0x07, 0x08, 0x10, 0x1f}},
{{0x1f, 0x08, 0x18, 0x1f}},
{{0x1b, 0x04, 0x0c, 0x13}},
{{0x03, 0x1c, 0x04, 0x03}},
{{0x19, 0x15, 0x15, 0x13}},
{{0x00, 0x1f, 0x11, 0x00}},
{{0x02, 0x04, 0x08, 0x10}},
{{0x00, 0x11, 0x1f, 0x00}},
{{0x02, 0x01, 0x02, 0x00}},
{{0x10, 0x10, 0x10, 0x10}},
{{0x00, 0x01, 0x02, 0x00}},
{{0x19, 0x15, 0x15, 0x1e}},
{{0x1f, 0x14, 0x14, 0x08}},
{{0x0c, 0x12, 0x12, 0x12}},
{{0x08, 0x14, 0x14, 0x1f}},
{{0x0e, 0x15, 0x15, 0x16}},
{{0x04, 0x1e, 0x05, 0x01}},
{{0x13, 0x15, 0x15, 0x0f}},
{{0x1f, 0x04, 0x04, 0x18}},
{{0x00, 0x14, 0x1d, 0x10}},
{{0x18, 0x10, 0x1d, 0x00}},
{{0x1f, 0x08, 0x14, 0x12}},
{{0x11, 0x1f, 0x10, 0x00}},
{{0x1e, 0x06, 0x06, 0x1e}},
{{0x1e, 0x02, 0x02, 0x1c}},
{{0x0c, 0x12, 0x12, 0x0c}},
{{0x1f, 0x05, 0x05, 0x02}},
{{0x02, 0x05, 0x05, 0x1f}},
{{0x1e, 0x04, 0x02, 0x06}},
{{0x10, 0x16, 0x1a, 0x02}},
{{0x02, 0x0f, 0x12, 0x10}},
{{0x0e, 0x10, 0x10, 0x1e}},
{{0x06, 0x08, 0x10, 0x0e}},
{{0x1e, 0x18, 0x18, 0x1e}},
{{0x12, 0x0c, 0x0c, 0x12}},
{{0x17, 0x14, 0x14, 0x0f}},
{{0x12, 0x1a, 0x16, 0x12}},
{{0x00, 0x04, 0x1f, 0x11}},
{{0x00, 0x00, 0x1f, 0x00}},
{{0x00, 0x11, 0x1f, 0x04}},
{{0x02, 0x04, 0x02, 0x04}},
};
static const struct utf_glyph4x PROGMEM font45_extra[] = {
{.utf={.symbol="<EFBFBD>"}, {{0x1f, 0x11, 0x11, 0x1f}}},
{.utf={.symbol="°"}, {{0x07, 0x05, 0x07, 0x00}}},
};

@ -0,0 +1,116 @@
static const font5x_bitmap_t PROGMEM font57_ascii[] = {
{{0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x00, 0x00, 0x5f, 0x00, 0x00}},
{{0x00, 0x07, 0x00, 0x07, 0x00}},
{{0x14, 0x7f, 0x14, 0x7f, 0x14}},
{{0x24, 0x2a, 0x7f, 0x2a, 0x12}},
{{0x23, 0x13, 0x08, 0x64, 0x62}},
{{0x36, 0x49, 0x55, 0x22, 0x50}},
{{0x00, 0x05, 0x03, 0x00, 0x00}},
{{0x00, 0x1c, 0x22, 0x41, 0x00}},
{{0x00, 0x41, 0x22, 0x1c, 0x00}},
{{0x14, 0x08, 0x3e, 0x08, 0x14}},
{{0x08, 0x08, 0x3e, 0x08, 0x08}},
{{0x00, 0x50, 0x30, 0x00, 0x00}},
{{0x08, 0x08, 0x08, 0x08, 0x08}},
{{0x00, 0x60, 0x60, 0x00, 0x00}},
{{0x20, 0x10, 0x08, 0x04, 0x02}},
{{0x3e, 0x51, 0x49, 0x45, 0x3e}},
{{0x00, 0x42, 0x7f, 0x40, 0x00}},
{{0x42, 0x61, 0x51, 0x49, 0x46}},
{{0x21, 0x41, 0x45, 0x4b, 0x31}},
{{0x18, 0x14, 0x12, 0x7f, 0x10}},
{{0x27, 0x45, 0x45, 0x45, 0x39}},
{{0x3c, 0x4a, 0x49, 0x49, 0x30}},
{{0x01, 0x71, 0x09, 0x05, 0x03}},
{{0x36, 0x49, 0x49, 0x49, 0x36}},
{{0x06, 0x49, 0x49, 0x29, 0x1e}},
{{0x00, 0x36, 0x36, 0x00, 0x00}},
{{0x00, 0x56, 0x36, 0x00, 0x00}},
{{0x08, 0x14, 0x22, 0x41, 0x00}},
{{0x14, 0x14, 0x14, 0x14, 0x14}},
{{0x00, 0x41, 0x22, 0x14, 0x08}},
{{0x02, 0x01, 0x51, 0x09, 0x06}},
{{0x32, 0x49, 0x79, 0x41, 0x3e}},
{{0x7e, 0x11, 0x11, 0x11, 0x7e}},
{{0x7f, 0x49, 0x49, 0x49, 0x36}},
{{0x3e, 0x41, 0x41, 0x41, 0x22}},
{{0x7f, 0x41, 0x41, 0x22, 0x1c}},
{{0x7f, 0x49, 0x49, 0x49, 0x41}},
{{0x7f, 0x09, 0x09, 0x09, 0x01}},
{{0x3e, 0x41, 0x49, 0x49, 0x7a}},
{{0x7f, 0x08, 0x08, 0x08, 0x7f}},
{{0x00, 0x41, 0x7f, 0x41, 0x00}},
{{0x20, 0x40, 0x41, 0x3f, 0x01}},
{{0x7f, 0x08, 0x14, 0x22, 0x41}},
{{0x7f, 0x40, 0x40, 0x40, 0x40}},
{{0x7f, 0x02, 0x0c, 0x02, 0x7f}},
{{0x7f, 0x04, 0x08, 0x10, 0x7f}},
{{0x3e, 0x41, 0x41, 0x41, 0x3e}},
{{0x7f, 0x09, 0x09, 0x09, 0x06}},
{{0x3e, 0x41, 0x51, 0x21, 0x5e}},
{{0x7f, 0x09, 0x19, 0x29, 0x46}},
{{0x46, 0x49, 0x49, 0x49, 0x31}},
{{0x01, 0x01, 0x7f, 0x01, 0x01}},
{{0x3f, 0x40, 0x40, 0x40, 0x3f}},
{{0x1f, 0x20, 0x40, 0x20, 0x1f}},
{{0x3f, 0x40, 0x38, 0x40, 0x3f}},
{{0x63, 0x14, 0x08, 0x14, 0x63}},
{{0x07, 0x08, 0x70, 0x08, 0x07}},
{{0x61, 0x51, 0x49, 0x45, 0x43}},
{{0x00, 0x7f, 0x41, 0x41, 0x00}},
{{0x02, 0x04, 0x08, 0x10, 0x20}},
{{0x00, 0x41, 0x41, 0x7f, 0x00}},
{{0x04, 0x02, 0x01, 0x02, 0x04}},
{{0x40, 0x40, 0x40, 0x40, 0x40}},
{{0x00, 0x01, 0x02, 0x04, 0x00}},
{{0x20, 0x54, 0x54, 0x54, 0x78}},
{{0x7f, 0x48, 0x44, 0x44, 0x38}},
{{0x38, 0x44, 0x44, 0x44, 0x20}},
{{0x38, 0x44, 0x44, 0x48, 0x7f}},
{{0x38, 0x54, 0x54, 0x54, 0x18}},
{{0x08, 0x7e, 0x09, 0x01, 0x02}},
{{0x0c, 0x52, 0x52, 0x52, 0x3e}},
{{0x7f, 0x08, 0x04, 0x04, 0x78}},
{{0x00, 0x44, 0x7d, 0x40, 0x00}},
{{0x20, 0x40, 0x44, 0x3d, 0x00}},
{{0x7f, 0x10, 0x28, 0x44, 0x00}},
{{0x00, 0x41, 0x7f, 0x40, 0x00}},
{{0x7c, 0x04, 0x18, 0x04, 0x78}},
{{0x7c, 0x08, 0x04, 0x04, 0x78}},
{{0x38, 0x44, 0x44, 0x44, 0x38}},
{{0x7c, 0x14, 0x14, 0x14, 0x08}},
{{0x08, 0x14, 0x14, 0x18, 0x7c}},
{{0x7c, 0x08, 0x04, 0x04, 0x08}},
{{0x48, 0x54, 0x54, 0x54, 0x20}},
{{0x04, 0x3f, 0x44, 0x40, 0x20}},
{{0x3c, 0x40, 0x40, 0x20, 0x7c}},
{{0x1c, 0x20, 0x40, 0x20, 0x1c}},
{{0x3c, 0x40, 0x30, 0x40, 0x3c}},
{{0x44, 0x28, 0x10, 0x28, 0x44}},
{{0x0c, 0x50, 0x50, 0x50, 0x3c}},
{{0x44, 0x64, 0x54, 0x4c, 0x44}},
{{0x00, 0x08, 0x36, 0x41, 0x00}},
{{0x00, 0x00, 0x7f, 0x00, 0x00}},
{{0x00, 0x41, 0x36, 0x08, 0x00}},
{{0x10, 0x08, 0x08, 0x10, 0x08}},
};
static const struct utf_glyph5x PROGMEM font57_extra[] = {
{.utf={.symbol="<EFBFBD>"}, {{0x7f, 0x41, 0x41, 0x41, 0x7f}}},
{.utf={.symbol="×"}, {{0x22, 0x14, 0x08, 0x14, 0x22}}},
{.utf={.symbol=""}, {{0x08, 0x04, 0x3e, 0x04, 0x08}}},
{.utf={.symbol=""}, {{0x08, 0x10, 0x3e, 0x10, 0x08}}},
{.utf={.symbol=""}, {{0x08, 0x1c, 0x2a, 0x08, 0x08}}},
{.utf={.symbol=""}, {{0x08, 0x08, 0x2a, 0x1c, 0x08}}},
{.utf={.symbol=""}, {{0x1c, 0x22, 0x2e, 0x2a, 0x1c}}},
{.utf={.symbol=""}, {{0x63, 0x55, 0x4d, 0x55, 0x63}}},
{.utf={.symbol=""}, {{0x1c, 0x22, 0x2a, 0x22, 0x1c}}},
{.utf={.symbol=""}, {{0x10, 0x38, 0x54, 0x10, 0x1e}}},
{.utf={.symbol="🌡"}, {{0x20, 0x7e, 0x79, 0x7e, 0x2a}}},
{.utf={.symbol="°"}, {{0x00, 0x07, 0x05, 0x07, 0x00}}},
{.utf={.symbol="μ"}, {{0x7c, 0x20, 0x20, 0x10, 0x3c}}},
{.utf={.symbol="🔙"}, {{0x04, 0x4e, 0x55, 0x44, 0x38}}},
{.utf={.symbol=""}, {{0x7f, 0x3e, 0x1c, 0x08, 0x00}}},
{.utf={.symbol=""}, {{0x00, 0x08, 0x1c, 0x3e, 0x7f}}},
};

@ -0,0 +1,598 @@
#define FONT_W 3
#define FONT_H 5
const char* font_ascii[] = {
// 32 " "
" ",
" ",
" ",
" ",
" ",
// 33 "!"
" X ",
" X ",
" X ",
" ",
" X ",
// 34 """
"X X",
"X X",
" ",
" ",
" ",
// 35 "#"
" X ",
"XXX",
" X ",
"XXX",
" X ",
// 36 "$"
" X ",
"XXX",
"XX ",
" XX",
"XXX",
// 37 "%"
"X X",
" X",
" X ",
"X ",
"X x",
// 38 "&"
"xxx",
" x ",
"xx ",
"x x",
"xxx",
// 39 "'"
" x ",
" x ",
" ",
" ",
" ",
// 40 "("
" x ",
"x ",
"x ",
"x ",
" x ",
// 41 ")"
"x ",
" x ",
" x ",
" x ",
"x ",
// 42 "*"
"x x",
" x ",
"x x",
" ",
" ",
// 43 "+"
" ",
" x ",
"xxx",
" x ",
" ",
// 44 ","
" ",
" ",
" ",
" x ",
"x ",
// 45 "-"
" ",
" ",
"xxx",
" ",
" ",
// 46 "."
" ",
" ",
" ",
"xx ",
"xx ",
// 47 "/"
" x",
" x",
" x ",
"x ",
"x ",
// 48 "0"
"xxx",
"x x",
"x x",
"x x",
"xxx",
// 49 "1"
" x ",
"xx ",
" x ",
" x ",
"xxx",
// 50 "2"
"xxx",
" x",
"xxx",
"x ",
"xxx",
// 51 "3"
"xxx",
" x",
" xx",
" x",
"xxx",
// 52 "4"
"x x",
"x x",
"xxx",
" x",
" x",
// 53 "5"
"xxx",
"x ",
"xxx",
" x",
"xxx",
// 54 "6"
"xxx",
"x ",
"xxx",
"x x",
"xxx",
// 55 "7"
"xxx",
"x x",
" x",
" x",
" x",
// 56 "8"
"xxx",
"x x",
"xxx",
"x x",
"xxx",
// 57 "9"
"xxx",
"x x",
"xxx",
" x",
"xxx",
// 58 ":"
" ",
" x ",
" ",
" x ",
" ",
// 59 ";"
" ",
" x ",
" ",
" x ",
"x ",
// 60 "<"
" x",
" x ",
"x ",
" x ",
" x",
// 61 "="
" ",
"xxx",
" ",
"xxx",
" ",
// 62 ">"
"x ",
" x ",
" x",
" x ",
"x ",
// 63 "?"
"xxx",
" x",
" xx",
" ",
" x ",
// 64 "@"
"xxx",
"x x",
"x x",
"x ",
" xx",
// 65 "A"
"xxx",
"x x",
"xxx",
"x x",
"x x",
// 66 "B"
"xx ",
"x x",
"xxx",
"x x",
"xxx",
// 67 "C"
"xxx",
"x ",
"x ",
"x ",
"xxx",
// 68 "D"
"xx ",
"x x",
"x x",
"x x",
"xx ",
// 69 "E"
"xxx",
"x ",
"xx ",
"x ",
"xxx",
// 70 "F"
"xxx",
"x ",
"xx ",
"x ",
"x ",
// 71 "G"
"xxx",
"x ",
"x x",
"x x",
"xxx",
// 72 "H"
"x x",
"x x",
"xxx",
"x x",
"x x",
// 73 "I"
"xxx",
" x ",
" x ",
" x ",
"xxx",
// 74 "J"
"xxx",
" x",
" x",
"x x",
"xxx",
// 75 "K"
"x x",
"x x",
"xx ",
"x x",
"x x",
// 76 "L"
"x ",
"x ",
"x ",
"x ",
"xxx",
// 77 "M"
"xxx",
"xxx",
"x x",
"x x",
"x x",
// 78 "N"
"xx ",
"x x",
"x x",
"x x",
"x x",
// 79 "O"
"xxx",
"x x",
"x x",
"x x",
"xxx",
// 80 "P"
"xxx",
"x x",
"xxx",
"x ",
"x ",
// 81 "Q"
"xxx",
"x x",
"x x",
"xxx",
" x",
// 82 "R"
"xxx",
"x x",
"xxx",
"xx ",
"x x",
// 83 "S"
"xxx",
"x ",
"xxx",
" x",
"xxx",
// 84 "T"
"xxx",
" x ",
" x ",
" x ",
" x ",
// 85 "U"
"x x",
"x x",
"x x",
"x x",
"xxx",
// 86 "V"
"x x",
"x x",
"x x",
"x x",
" x ",
// 87 "W"
"x x",
"x x",
"x x",
"xxx",
"xxx",
// 88 "X"
"x x",
"x x",
" x ",
"x x",
"x x",
// 89 "Y"
"x x",
"x x",
"xxx",
" x ",
" x ",
// 90 "Z"
"xxx",
" x",
" x ",
"x ",
"xxx",
// 91 "["
" xx",
" x ",
" x ",
" x ",
" xx",
// 92 "\"
"x ",
"x ",
" x ",
" x",
" x",
// 93 "]"
"xx ",
" x ",
" x ",
" x ",
"xx ",
// 94 "^"
" x ",
"x x",
" ",
" ",
" ",
// 95 "_"
" ",
" ",
" ",
" ",
"xxx",
// 96 "`"
"x ",
" x ",
" ",
" ",
" ",
// 97 "a"
"xx ",
" x",
"xxx",
"x x",
"xxx",
// 98 "b"
"x ",
"x ",
"xxx",
"x x",
"xxx",
// 99 "c"
" ",
" ",
"xxx",
"x ",
"xxx",
// 100 "d"
" x",
" x",
"xxx",
"x x",
"xxx",
// 101 "e"
"xxx",
"x x",
"xxx",
"x ",
" xx",
// 102 "f"
" xx",
" x ",
"xxx",
" x ",
" x ",
// 103 "g"
"xxx",
"x x",
"xxx",
" x",
"xx ",
// 104 "h"
"x ",
"x ",
"xxx",
"x x",
"x x",
// 105 "i"
" x ",
" ",
"xx ",
" x ",
"xxx",
// 106 "j"
" x",
" ",
" x",
"x x",
"xxx",
// 107 "k"
"x ",
"x ",
"x x",
"xx ",
"x x",
// 108 "l"
"xx ",
" x ",
" x ",
" x ",
"xxx",
// 109 "m"
" ",
" ",
"xxx",
"xxx",
"x x",
// 110 "n"
" ",
" ",
"xx ",
"x x",
"x x",
// 111 "o"
" ",
" ",
"xxx",
"x x",
"xxx",
// 112 "p"
" ",
"xxx",
"x x",
"xxx",
"x ",
// 113 "q"
" ",
"xxx",
"x x",
"xxx",
" x",
// 114 "r"
" ",
" ",
"xxx",
"x ",
"x ",
// 115 "s"
" ",
"xxx",
"xx ",
" x",
"xxx",
// 116 "t"
" x ",
"xxx",
" x ",
" x ",
" xx",
// 117 "u"
" ",
"x z",
"x x",
"x x",
"xxx",
// 118 "v"
" ",
"x x",
"x x",
"x x",
" x ",
// 119 "w"
" ",
" ",
"x x",
"xxx",
"xxx",
// 120 "x"
" ",
" ",
"x x",
" x ",
"x x",
// 121 "y"
" ",
"x x",
"xxx",
" x",
"xxx",
// 122 "z"
" ",
"xxx",
" xx",
"x ",
"xxx",
// 123 "{"
" x",
" x ",
"xx ",
" x ",
" x",
// 124 "|"
" x ",
" x ",
" x ",
" x ",
" x ",
// 125 "}"
"x ",
" x ",
" xx",
" x ",
"x ",
// 126 "~"
" ",
" xx",
"x x",
" ",
" ",
};
const char *font_extras[] = {
// Extras
// Extra 0 "°"
"xxx",
"xxx",
"xxx",
"xxx",
"xxx",
// Extra 1 "°"
"xxx",
"x x",
"xxx",
" ",
" ",
};
const char *font_extras_utf[] = {
"<EFBFBD>",
"°",
};
#include "fontedit_render.inc.c"

@ -0,0 +1,598 @@
#define FONT_W 4
#define FONT_H 5
const char *font_ascii[] = {
// 32 " "
" ",
" ",
" ",
" ",
" ",
// 33 "!"
" xx ",
" xx ",
" xx ",
" ",
" xx ",
// 34 """
"x x ",
"x x ",
" ",
" ",
" ",
// 35 "#"
" x x",
"xxxx",
" x x",
"xxxx",
" x x",
// 36 "$"
" xxx",
"x x ",
" xx ",
" x x",
"xxx ",
// 37 "%"
" ",
"x x",
" x ",
" x ",
"x x",
// 38 "&"
"xxx ",
"x x ",
"xxxx",
"x x ",
"xxxx",
// 39 "'"
" x ",
" x ",
" ",
" ",
" ",
// 40 "("
" x ",
" x ",
" x ",
" x ",
" x ",
// 41 ")"
" x ",
" x ",
" x ",
" x ",
" x ",
// 42 "*"
"x x",
" xx ",
"x x",
" ",
" ",
// 43 "+"
" ",
" x ",
"xxx ",
" x ",
" ",
// 44 ","
" ",
" ",
" ",
" x ",
" x ",
// 45 "-"
" ",
" ",
" ###",
" ",
" ",
// 46 "."
" ",
" ",
" ",
" ## ",
" ## ",
// 47 "/"
" ",
" x",
" x ",
" x ",
"x ",
// 48 "0"
" ## ",
"# #",
"# #",
"# #",
" ## ",
// 49 "1"
" # ",
" ## ",
"# # ",
" # ",
" ###",
// 50 "2"
" ## ",
"# #",
" # ",
" # ",
"####",
// 51 "3"
" ## ",
"# #",
" # ",
"# #",
" ## ",
// 52 "4"
"# ",
"# # ",
"####",
" # ",
" # ",
// 53 "5"
"####",
"# ",
"### ",
" #",
"### ",
// 54 "6"
" ###",
"# ",
"### ",
"# #",
" ## ",
// 55 "7"
"####",
" #",
" # ",
" # ",
"# ",
// 56 "8"
" ## ",
"# #",
" ## ",
"# #",
" ## ",
// 57 "9"
" ## ",
"# #",
" ## ",
" #",
"### ",
// 58 ":"
" ",
" x ",
" ",
" x ",
" ",
// 59 ";"
" ",
" x ",
" ",
" x ",
" x ",
// 60 "<"
" x ",
" x ",
"x ",
" x ",
" x ",
// 61 "="
" ",
"xxx ",
" ",
"xxx ",
" ",
// 62 ">"
"x ",
" x ",
" x ",
" x ",
"x ",
// 63 "?"
"xxx ",
" x",
" x ",
" ",
" x ",
// 64 "@"
"xxx ",
"x xx",
"x xx",
"x ",
"xxxx",
// 65 "A"
" xx ",
"x x",
"xxxx",
"x x",
"x x",
// 66 "B"
"xxx ",
"x x",
"xxx ",
"x x",
"xxx ",
// 67 "C"
" ## ",
"# #",
"# ",
"# #",
" ## ",
// 68 "D"
"xxx ",
"x x",
"x x",
"x x",
"xxx ",
// 69 "E"
"xxxx",
"x ",
"xxx ",
"x ",
"xxxx",
// 70 "F"
"xxxx",
"x ",
"xxx ",
"x ",
"x ",
// 71 "G"
" xxx",
"x ",
"x xx",
"x x",
" xx ",
// 72 "H"
"x x",
"x x",
"xxxx",
"x x",
"x x",
// 73 "I"
" xxx",
" x ",
" x ",
" x ",
" xxx",
// 74 "J"
"xxxx",
" x",
" x",
"x x",
" xx ",
// 75 "K"
"x x",
"x x ",
"xx ",
"x x ",
"x x",
// 76 "L"
"x ",
"x ",
"x ",
"x ",
"xxxx",
// 77 "M"
"#XX#",
"#X #",
"# #",
"# #",
"# #",
// 78 "N"
"x x",
"xx x",
"x xx",
"x x",
"x x",
// 79 "O"
" xx ",
"x x",
"x x",
"x x",
" xx ",
// 80 "P"
"### ",
"# #",
"### ",
"# ",
"# ",
// 81 "Q"
" xx ",
"x x",
"x x",
"x xx",
" xxx",
// 82 "R"
"xxx ",
"x x",
"xxx ",
"X x ",
"X x",
// 83 "S"
" xxx",
"x ",
" xx ",
" x",
"xxx ",
// 84 "T"
"xxx ",
" x ",
" x ",
" x ",
" x ",
// 85 "U"
"x x",
"x x",
"x x",
"x x",
" xx ",
// 86 "V"
"x x",
"x x",
"x x",
" x x",
" xx",
// 87 "W"
"x x",
"x x",
"x x",
"xxxx",
"x xx",
// 88 "X"
"x x",
"x x",
" xx ",
"x x ",
"x x",
// 89 "Y"
"x x",
"x x",
" xx ",
" x ",
" x ",
// 90 "Z"
"xxxx",
" x",
" xx ",
"x ",
"xxxx",
// 91 "["
" xx ",
" x ",
" x ",
" x ",
" xx ",
// 92 "\"
" ",
"x ",
" x ",
" x ",
" x",
// 93 "]"
" xx ",
" x ",
" x ",
" x ",
" xx ",
// 94 "^"
" x ",
"x x ",
" ",
" ",
" ",
// 95 "_"
" ",
" ",
" ",
" ",
"xxxx",
// 96 "`"
" x ",
" x ",
" ",
" ",
" ",
// 97 "a"
"xxx ",
" x",
" xxx",
"x x",
"xxxx",
// 98 "b"
"x ",
"x ",
"xxx ",
"x x",
"xxx ",
// 99 "c"
" ",
" xxx",
"x ",
"x ",
" xxx",
// 100 "d"
" x",
" x",
" xxx",
"x x",
" xxx",
// 101 "e"
" xx ",
"x x",
"xxxx",
"x ",
" xxx",
// 102 "f"
" xx",
" x ",
"xxx ",
" x ",
" x ",
// 103 "g"
"xxxx",
"x x",
" xxx",
" x",
"xxx ",
// 104 "h"
"x ",
"x ",
"xxx ",
"x x",
"x x",
// 105 "i"
" x ",
" ",
" xx ",
" x ",
" xxx",
// 106 "j"
" x ",
" ",
" x ",
"x x ",
"xxx ",
// 107 "k"
"x ",
"x x",
"x x ",
"xx ",
"x xx",
// 108 "l"
"xx ",
" x ",
" x ",
" x ",
"xxx ",
// 109 "m"
" ",
"xxxx",
"xxxx",
"x x",
"x x",
// 110 "n"
" ",
"xxx ",
"x x",
"x x",
"x x",
// 111 "o"
" ",
" xx ",
"x x",
"x x",
" xx ",
// 112 "p"
"xxx ",
"x x",
"xxx ",
"x ",
"x ",
// 113 "q"
" xxx",
"x x",
" xxx",
" x",
" x",
// 114 "r"
" ",
"x xx",
"xx x",
"x ",
"x ",
// 115 "s"
" ",
" xxx",
" x ",
" x ",
"xxx ",
// 116 "t"
" x ",
"xxx ",
" x ",
" x ",
" xx",
// 117 "u"
" ",
"x x",
"x x",
"x x",
" xxx",
// 118 "v"
" ",
"x x",
"x x",
" x x",
" x ",
// 119 "w"
" ",
"x x",
"x x",
"xxxx",
"xxxx",
// 120 "x"
" ",
"x x",
" xx ",
" xx ",
"x x",
// 121 "y"
"x x",
"x x",
"xxxx",
" x",
"xxx ",
// 122 "z"
" ",
"xxxx",
" x ",
" x ",
"xxxx",
// 123 "{"
" xx",
" x ",
" xx ",
" x ",
" xx",
// 124 "|"
" x ",
" x ",
" x ",
" x ",
" x ",
// 125 "}"
" xx ",
" x ",
" xx",
" x ",
" xx ",
// 126 "~"
" ",
"x x ",
" x x",
" ",
" ",
};
const char *font_extras[] = {
// Extra 0 - unknown symbol
"####",
"# #",
"# #",
"# #",
"####",
// Extra 1 "°"
"xxx ",
"# # ",
"xxx ",
" ",
" ",
};
const char *font_extras_utf[] = {
"<EFBFBD>",
"°",
};
#include "fontedit_render.inc.c"

@ -0,0 +1,919 @@
#define FONT_W 5
#define FONT_H 7
const char *font_ascii[] = {
// 32 " "
" ",
" ",
" ",
" ",
" ",
" ",
" ",
// 33 "!"
" # ",
" # ",
" # ",
" # ",
" # ",
" ",
" # ",
// 34 """
" # # ",
" # # ",
" # # ",
" ",
" ",
" ",
" ",
// 35 "#"
" # # ",
" # # ",
"#####",
" # # ",
"#####",
" # # ",
" # # ",
// 36 "$"
" # ",
" ####",
"# # ",
" ### ",
" # #",
"#### ",
" # ",
// 37 "%"
"## ",
"## #",
" # ",
" # ",
" # ",
"# ##",
" ##",
// 38 "&"
" ## ",
"# # ",
"# # ",
" # ",
"# # #",
"# # ",
" ## #",
// 39 "'"
" ## ",
" # ",
" # ",
" ",
" ",
" ",
" ",
// 40 "("
" # ",
" # ",
" # ",
" # ",
" # ",
" # ",
" # ",
// 41 ")"
" # ",
" # ",
" # ",
" # ",
" # ",
" # ",
" # ",
// 42 "*"
" ",
" # ",
"# # #",
" ### ",
"# # #",
" # ",
" ",
// 43 "+"
" ",
" # ",
" # ",
"#####",
" # ",
" # ",
" ",
// 44 ","
" ",
" ",
" ",
" ",
" ## ",
" # ",
" # ",
// 45 "-"
" ",
" ",
" ",
"#####",
" ",
" ",
" ",
// 46 "."
" ",
" ",
" ",
" ",
" ",
" ## ",
" ## ",
// 47 "/"
" ",
" #",
" # ",
" # ",
" # ",
"# ",
" ",
// 48 "0"
" ### ",
"# #",
"# ##",
"# # #",
"## #",
"# #",
" ### ",
// 49 "1"
" # ",
" ## ",
" # ",
" # ",
" # ",
" # ",
" ### ",
// 50 "2"
" ### ",
"# #",
" #",
" # ",
" # ",
" # ",
"#####",
// 51 "3"
"#####",
" # ",
" # ",
" # ",
" #",
"# #",
" ### ",
// 52 "4"
" # ",
" ## ",
" # # ",
"# # ",
"#####",
" # ",
" # ",
// 53 "5"
"#####",
"# ",
"#### ",
" #",
" #",
"# #",
" ### ",
// 54 "6"
" ## ",
" # ",
"# ",
"#### ",
"# #",
"# #",
" ### ",
// 55 "7"
"#####",
" #",
" # ",
" # ",
" # ",
" # ",
" # ",
// 56 "8"
" ### ",
"# #",
"# #",
" ### ",
"# #",
"# #",
" ### ",
// 57 "9"
" ### ",
"# #",
"# #",
" ####",
" #",
" # ",
" ## ",
// 58 ":"
" ",
" ## ",
" ## ",
" ",
" ## ",
" ## ",
" ",
// 59 ";"
" ",
" ## ",
" ## ",
" ",
" ## ",
" # ",
" # ",
// 60 "<"
" # ",
" # ",
" # ",
"# ",
" # ",
" # ",
" # ",
// 61 "="
" ",
" ",
"#####",
" ",
"#####",
" ",
" ",
// 62 ">"
" # ",
" # ",
" # ",
" #",
" # ",
" # ",
" # ",
// 63 "?"
" ### ",
"# #",
" #",
" # ",
" # ",
" ",
" # ",
// 64 "@"
" ### ",
"# #",
" #",
" ## #",
"# # #",
"# # #",
" ### ",
// 65 "A"
" ### ",
"# #",
"# #",
"# #",
"#####",
"# #",
"# #",
// 66 "B"
"#### ",
"# #",
"# #",
"#### ",
"# #",
"# #",
"#### ",
// 67 "C"
" ### ",
"# #",
"# ",
"# ",
"# ",
"# #",
" ### ",
// 68 "D"
"### ",
"# # ",
"# #",
"# #",
"# #",
"# # ",
"### ",
// 69 "E"
"#####",
"# ",
"# ",
"#### ",
"# ",
"# ",
"#####",
// 70 "F"
"#####",
"# ",
"# ",
"#### ",
"# ",
"# ",
"# ",
// 71 "G"
" ### ",
"# #",
"# ",
"# ###",
"# #",
"# #",
" ####",
// 72 "H"
"# #",
"# #",
"# #",
"#####",
"# #",
"# #",
"# #",
// 73 "I"
" ### ",
" # ",
" # ",
" # ",
" # ",
" # ",
" ### ",
// 74 "J"
" ###",
" # ",
" # ",
" # ",
" # ",
"# # ",
" ## ",
// 75 "K"
"# #",
"# # ",
"# # ",
"## ",
"# # ",
"# # ",
"# #",
// 76 "L"
"# ",
"# ",
"# ",
"# ",
"# ",
"# ",
"#####",
// 77 "M"
"# #",
"## ##",
"# # #",
"# # #",
"# #",
"# #",
"# #",
// 78 "N"
"# #",
"# #",
"## #",
"# # #",
"# ##",
"# #",
"# #",
// 79 "O"
" ### ",
"# #",
"# #",
"# #",
"# #",
"# #",
" ### ",
// 80 "P"
"#### ",
"# #",
"# #",
"#### ",
"# ",
"# ",
"# ",
// 81 "Q"
" ### ",
"# #",
"# #",
"# #",
"# # #",
"# # ",
" ## #",
// 82 "R"
"#### ",
"# #",
"# #",
"#### ",
"# # ",
"# # ",
"# #",
// 83 "S"
" ####",
"# ",
"# ",
" ### ",
" #",
" #",
"#### ",
// 84 "T"
"#####",
" # ",
" # ",
" # ",
" # ",
" # ",
" # ",
// 85 "U"
"# #",
"# #",
"# #",
"# #",
"# #",
"# #",
" ### ",
// 86 "V"
"# #",
"# #",
"# #",
"# #",
"# #",
" # # ",
" # ",
// 87 "W"
"# #",
"# #",
"# #",
"# # #",
"# # #",
"# # #",
" # # ",
// 88 "X"
"# #",
"# #",
" # # ",
" # ",
" # # ",
"# #",
"# #",
// 89 "Y"
"# #",
"# #",
"# #",
" # # ",
" # ",
" # ",
" # ",
// 90 "Z"
"#####",
" #",
" # ",
" # ",
" # ",
"# ",
"#####",
// 91 "["
" ### ",
" # ",
" # ",
" # ",
" # ",
" # ",
" ### ",
// 92 "\"
" ",
"# ",
" # ",
" # ",
" # ",
" #",
" ",
// 93 "]"
" ### ",
" # ",
" # ",
" # ",
" # ",
" # ",
" ### ",
// 94 "^"
" # ",
" # # ",
"# #",
" ",
" ",
" ",
" ",
// 95 "_"
" ",
" ",
" ",
" ",
" ",
" ",
"#####",
// 96 "`"
" # ",
" # ",
" # ",
" ",
" ",
" ",
" ",
// 97 "a"
" ",
" ",
" ### ",
" #",
" ####",
"# #",
" ####",
// 98 "b"
"# ",
"# ",
"# ## ",
"## #",
"# #",
"# #",
"#### ",
// 99 "c"
" ",
" ",
" ### ",
"# ",
"# ",
"# #",
" ### ",
// 100 "d"
" #",
" #",
" ## #",
"# ##",
"# #",
"# #",
" ####",
// 101 "e"
" ",
" ",
" ### ",
"# #",
"#####",
"# ",
" ### ",
// 102 "f"
" ## ",
" # #",
" # ",
"### ",
" # ",
" # ",
" # ",
// 103 "g"
" ",
" ####",
"# #",
"# #",
" ####",
" #",
" ### ",
// 104 "h"
"# ",
"# ",
"# ## ",
"## #",
"# #",
"# #",
"# #",
// 105 "i"
" # ",
" ",
" ## ",
" # ",
" # ",
" # ",
" ### ",
// 106 "j"
" # ",
" ",
" ## ",
" # ",
" # ",
"# # ",
" ## ",
// 107 "k"
"# ",
"# ",
"# # ",
"# # ",
"## ",
"# # ",
"# # ",
// 108 "l"
" ## ",
" # ",
" # ",
" # ",
" # ",
" # ",
" ### ",
// 109 "m"
" ",
" ",
"## # ",
"# # #",
"# # #",
"# #",
"# #",
// 110 "n"
" ",
" ",
"# ## ",
"## #",
"# #",
"# #",
"# #",
// 111 "o"
" ",
" ",
" ### ",
"# #",
"# #",
"# #",
" ### ",
// 112 "p"
" ",
" ",
"#### ",
"# #",
"#### ",
"# ",
"# ",
// 113 "q"
" ",
" ",
" ## #",
"# ##",
" ####",
" #",
" #",
// 114 "r"
" ",
" ",
"# ## ",
"## #",
"# ",
"# ",
"# ",
// 115 "s"
" ",
" ",
" ### ",
"# ",
" ### ",
" #",
"#### ",
// 116 "t"
" # ",
" # ",
"### ",
" # ",
" # ",
" # #",
" ## ",
// 117 "u"
" ",
" ",
"# #",
"# #",
"# #",
"# ##",
" ## #",
// 118 "v"
" ",
" ",
"# #",
"# #",
"# #",
" # # ",
" # ",
// 119 "w"
" ",
" ",
"# #",
"# #",
"# # #",
"# # #",
" # # ",
// 120 "x"
" ",
" ",
"# #",
" # # ",
" # ",
" # # ",
"# #",
// 121 "y"
" ",
" ",
"# #",
"# #",
" ####",
" #",
" ### ",
// 122 "z"
" ",
" ",
"#####",
" # ",
" # ",
" # ",
"#####",
// 123 "{"
" # ",
" # ",
" # ",
" # ",
" # ",
" # ",
" # ",
// 124 "|"
" # ",
" # ",
" # ",
" # ",
" # ",
" # ",
" # ",
// 125 "}"
" # ",
" # ",
" # ",
" # ",
" # ",
" # ",
" # ",
// 126 "~"
" ",
" ",
" ",
" ## #",
"# # ",
" ",
" ",
};
const char *font_extras[] = {
// Extras
// Extra 0
"#####",
"# #",
"# #",
"# #",
"# #",
"# #",
"#####",
// Extra 1
" ",
"# #",
" # # ",
" # ",
" # # ",
"# #",
" ",
// Extra 2
" ",
" # ",
" ### ",
"# # #",
" # ",
" # ",
" ",
// Extra 3
" ",
" # ",
" # ",
"# # #",
" ### ",
" # ",
" ",
// Extra 4
" ",
" # ",
" # ",
"#####",
" # ",
" # ",
" ",
// Extra 5
" ",
" # ",
" # ",
"#####",
" # ",
" # ",
" ",
// Extra 6
" ",
" ### ",
"# # #",
"# ###",
"# #",
" ### ",
" ",
// Extra 7
"#####",
"# #",
" ### ",
" # ",
" # # ",
"# #",
"#####",
// Extra 8
" ",
" ### ",
"# #",
"# # #",
"# #",
" ### ",
" ",
// Extra 9
" ",
" #",
" # #",
" # #",
"#####",
" # ",
" # ",
// Extra 10
" # ",
" # ##",
" # # ",
" ####",
" ### ",
"#####",
" ### ",
// Extra 11
" ### ",
" # # ",
" ### ",
" ",
" ",
" ",
" ",
// Extra 12
" ",
" ",
"# #",
"# #",
"# ##",
"### #",
"# ",
// Extra 13
" # ",
" # ",
"#### ",
" # #",
" # #",
" #",
" ### ",
// Extra 14
"# ",
"## ",
"### ",
"#### ",
"### ",
"## ",
"# ",
// Extra 15
" #",
" ##",
" ###",
" ####",
" ###",
" ##",
" #",
};
const char *font_extras_utf[] = {
"<EFBFBD>",
"×",
"",
"",
"",
"",
"", // clock
"",
"",
"",
"🌡",
"°",
"μ",
"🔙", // back
"",
"",
};
#include "fontedit_render.inc.c"

@ -0,0 +1,14 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
void main() {
printf("const char* symbols[] = {\n");
for(int c = 32; c < 127; c++) {
printf(" // %d \"%c\"\n", c, c);
for(int j=0;j<5;j++){
printf(" \" \",\n");
}
}
printf("};\n");
}

@ -0,0 +1,48 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
void generate_glyph_tables() {
printf("static const font%dx_bitmap_t PROGMEM font%d%d_ascii[] = {\n", FONT_W, FONT_W, FONT_H);
for (int i = 32; i < 127; i++) {
uint8_t line[8] = {};
for(int j = 0; j < FONT_H; j++) {
const char *row = font_ascii[(i - 32)*FONT_H + j];
for(int k = 0; k < FONT_W; k++) {
line[k] |= (row[k] != 32) << j;
}
}
printf(" {{");
for(int k = 0; k < FONT_W; k++) {
if (k > 0) printf(", ");
printf("0x%02x", line[k]);
}
printf("}},\n");
}
printf("};\n\n");
printf("static const struct utf_glyph%dx PROGMEM font%d%d_extra[] = {\n", FONT_W, FONT_W, FONT_H);
for (int i = 0; i < sizeof(font_extras) / (sizeof(font_extras[0]) * FONT_H); i++) {
uint8_t line[8] = {};
for(int j = 0; j < FONT_H; j++) {
const char *row = font_extras[i*FONT_H + j];
for(int k = 0; k < FONT_W; k++) {
line[k] |= (row[k] != 32) << j;
}
}
printf(" {.utf={.symbol=\"%s\"}, {{", font_extras_utf[i]);
for(int k = 0; k < FONT_W; k++) {
if (k > 0) printf(", ");
printf("0x%02x", line[k]);
}
printf("}}},\n");
}
printf("};\n");
}
void main() {
generate_glyph_tables();
}

@ -0,0 +1,282 @@
//
// Created by MightyPork on 2022/11/12.
//
#include "framebuffer.h"
#include "progmem.h"
#define MIN(a, b) ((a)>(b)?(b):(a))
#define MAX(a, b) ((a)>(b)?(a):(b))
#include <string.h>
uint8_t fb[FB_LEN];
/** Fill with a vertical pattern, 1 byte */
void fb_fill(uint8_t pattern)
{
memset(fb, pattern, sizeof(fb));
}
static void draw_mask(fbsize_t idx, uint8_t mask, fbcolor_t color)
{
if (color != 0) {
fb[idx] |= mask;
} else {
fb[idx] &= ~mask;
}
}
void fb_fill_pattern(const uint8_t *pattern, uint8_t pattern_len, fbcolor_t color)
{
uint8_t p = 0;
for (fbsize_t i = 0; i < FB_LEN; i++) {
draw_mask(i, pattern[p], color);
p++;
if (p >= pattern_len) {
p = 0;
}
}
}
void fb_invert()
{
for (fbsize_t i = 0; i < FB_LEN; i++) {
fb[i] ^= 0xFF;
}
}
void fb_px(fbpos_t x, fbpos_t y, fbcolor_t color)
{
if (x >= FBW || y >= FBH) { return; }
const fbpos_t row = y / 8;
const fbpos_t rowrem = y % 8;
const fbsize_t cell = (fbsize_t) x + (fbsize_t) row * FBW;
draw_mask(cell, 1 << rowrem, color);
}
uint8_t fb_getpx(fbpos_t x, fbpos_t y)
{
if (x >= FBW || y >= FBH) { return 0; }
const fbpos_t row = y / 8;
const fbpos_t rowrem = y % 8;
const fbsize_t cell = (fbsize_t) x + (fbsize_t) row * FBW;
if (fb[cell] & (1 << rowrem)) {
return 0xFF;
} else {
return 0x00;
}
}
void fb_hline(fbpos_t x, fbpos_t y, fbpos_t w, fbcolor_t color)
{
if (x >= FBW || y >= FBH) { return; }
w = MIN(FBW - x, w);
const fbpos_t row = y / 8;
const fbpos_t rowrem = y % 8;
fbsize_t cell = (fbsize_t) x + (fbsize_t) row * FBW;
for (uint8_t i = 0; i < w; i++) {
draw_mask(cell, 1 << rowrem, color);
cell++;
}
}
void fb_vline(fbpos_t x, fbpos_t y, fbpos_t h, fbcolor_t color)
{
if (x >= FBW || y >= FBH) { return; }
h = MIN(FBH - y - 1, h);
const fbpos_t row = y / 8;
const fbpos_t rowrem = y % 8;
uint16_t cell = (uint16_t) x + (uint16_t) row * FBW;
if (rowrem + h < 8) {
// all within one cell
const uint8_t mask = (0xFF << rowrem) & (0xFF >> (8 - h - rowrem));
draw_mask(cell, mask, color);
return;
} else {
// First
draw_mask(cell, 0xFF << rowrem, color);
h -= (rowrem == 0 ? 8 : (8 - rowrem));
const fbpos_t whole_cells = h / 8;
if (whole_cells > 0) {
h -= whole_cells * 8;
for (fbpos_t i = 0; i < whole_cells; i++) {
cell += FBW;
draw_mask(cell, 0xFF, color);
}
}
// last
cell += FBW;
draw_mask(cell, 0xFF >> (8 - h), color);
}
}
void fb_rect(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbcolor_t color)
{
if (x >= FBW || y >= FBH) { return; }
w = MIN(FBW - x, w);
h = MIN(FBH - y, h);
const fbpos_t row = y / 8;
const fbpos_t rowrem = y % 8;
fbsize_t cell = (fbsize_t) x + (fbsize_t) row * FBW;
if (w == 1 && h == 1) {
fb_px(x, y, color);
} else if (w == 1) {
fb_vline(x, y, h, color);
} else if (h == 1) {
fb_hline(x, y, w, color);
} else if (rowrem + h <= 8) {
// all within one cell
uint8_t mask = (0xFF << rowrem) & (0xFF >> (8 - h - rowrem));
for (fbpos_t i = 0; i < w; i++) {
draw_mask(cell + i, mask, color);
}
return;
} else {
// First
uint8_t mask = (0xFF << rowrem);
for (fbpos_t i = 0; i < w; i++) {
draw_mask(cell + i, mask, color);
}
h -= 8 - rowrem;
const fbpos_t whole_cells = h / 8;
if (whole_cells > 0) {
h -= whole_cells * 8;
for (fbpos_t j = 0; j < whole_cells; j++) {
cell += FBW;
for (fbpos_t i = 0; i < w; i++) {
draw_mask(cell + i, 0xFF, color);
}
}
}
cell += FBW;
// last
mask = (0xFF >> (8 - h));
for (fbpos_t i = 0; i < w; i++) {
draw_mask(cell + i, mask, color);
}
}
}
void fb_frame(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbpos_t thickness, fbcolor_t color)
{
if (thickness == 0) {
return;
} else if (thickness == 1) {
fb_hline(x, y, w, color);
fb_vline(x, y, h, color);
fb_hline(x, y + h - 1, w, color);
fb_vline(x + w - 1, y, h, color);
} else {
fb_rect(x, y, w, thickness, color);
fb_rect(x, y + h - thickness, w, thickness, color);
fb_rect(x, y + thickness, thickness, h - 2 * thickness, color);
fb_rect(x + w - thickness, y + thickness, thickness, h - 2 * thickness, color);
}
}
// setCircle draws a circle centered around x0,y0 with a defined
// radius. The circle can be black or white. And have a line
// thickness ranging from 1 to the radius of the circle.
// This function was grabbed from the SparkFun ColorLCDShield
// library.
void fb_circle(fbpos_t x0, fbpos_t y0, fbpos_t radius, uint8_t thickness, fbcolor_t color)
{
for (uint8_t r = 0; r < thickness; r++) {
int8_t f = 1 - radius;
fbpos_t ddF_x = 0;
int8_t ddF_y = -2 * radius;
fbpos_t x = 0;
fbpos_t y = radius;
fb_px(x0, y0 + radius, color);
fb_px(x0 + radius, y0, color);
if (y0 >= radius) {
fb_px(x0, y0 - radius, color);
}
if (x0 >= radius) {
fb_px(x0 - radius, y0, color);
}
while (x < y) {
if (f >= 0) {
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x + 1;
fb_px(x0 + x, y0 + y, color);
fb_px(x0 - x, y0 + y, color);
fb_px(x0 + x, y0 - y, color);
fb_px(x0 - x, y0 - y, color);
fb_px(x0 + y, y0 + x, color);
fb_px(x0 - y, y0 + x, color);
fb_px(x0 + y, y0 - x, color);
fb_px(x0 - y, y0 - x, color);
}
radius--;
}
}
#if IS_AVR
void fb_bitmap_P(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, const uint8_t *map, fbcolor_t color)
#else
void fb_bitmap(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, const uint8_t *map, fbcolor_t color)
#endif
{
if (x >= FBW || y >= FBH) { return; }
const fbpos_t w0 = w;
w = MIN(FBW - x - 1, w);
h = MIN(FBH - y - 1, h);
const fbpos_t row = y / 8;
const fbpos_t rowrem = y % 8;
fbsize_t cell = (fbsize_t) x + (fbsize_t) row * FBW;
if (rowrem + h <= 8) {
for (fbpos_t i = 0; i < w; i++) {
// all within one cell
const uint8_t mask = (pgm_read_byte(&map[i]) & (0xFF >> (8 - h))) << rowrem;
draw_mask(cell + i, mask, color);
}
return;
} else {
fbsize_t mapc0 = 0;
// Draw the bitmap slice-by-slice based on how rows of the bitmap intersect with rows of the canvas.
// This could be optimized to walk each row of the canvas only once, but the code would get bigger.
while (h > 0) {
for (fbpos_t i = 0; i < w; i++) {
const uint8_t mask = (pgm_read_byte(&map[i + mapc0]) & (0xFF >> rowrem)) << rowrem;
draw_mask(cell + i, mask, color);
}
cell += FBW;
if (rowrem != 0) {
for (fbpos_t i = 0; i < w; i++) {
const uint8_t mask = (pgm_read_byte(&map[i + mapc0]) & (0xFF << (8 - rowrem))) >> (8 - rowrem);
draw_mask(cell + i, mask, color);
}
}
if (h > 8) {
h -= 8;
} else {
break;
}
mapc0 += w0;
}
}
}

@ -0,0 +1,87 @@
//
// Created by MightyPork on 2022/11/12.
//
#ifndef FRAMEBUFFER_H
#define FRAMEBUFFER_H
#include <stdbool.h>
#include <stdint.h>
#include "framebuffer_config.h"
#define FBSET 0xFF
#define FBCLEAR 0x00
typedef uint16_t fbsize_t;
typedef uint8_t fbpos_t;
typedef uint8_t fbcolor_t;
#define FB_LEN ((FBH / 8) * FBW)
/// Framebuffer backing array.
///
/// The format is the native format for SSD1306: array of bytes representing pixels in 8 rows at once, LSB is the topmost row.
///
/// a0 b0 ... til the display width
/// a1 b1
/// a2 b2
/// ... ...
/// a7 b7
///
/// and more bytes continue rows 8-15 and so on
extern uint8_t fb[ FB_LEN ];
/// Fill the entire screen with a byte pattern.
/// Use 0xFF or 0x00 for a solid fill. Other patterns may be used to create horizontal stripes.
void fb_fill(uint8_t pattern);
/// Invert the entire screen
void fb_invert();
/// Clear the display (fill with 0x00)
static inline void fb_clear(void)
{
fb_fill(0);
}
/// Fill screen with a repeating pattern
///
/// \param pattern - bytes to repeat, PROGMEM
/// \param pattern_len - len of the pattern
void fb_fill_pattern(const uint8_t* pattern, uint8_t pattern_len, fbcolor_t color);
/// Set a single pixel
void fb_px(fbpos_t x, fbpos_t y, fbcolor_t color);
/// Get pixel color
uint8_t fb_getpx(fbpos_t x, fbpos_t y);
/// Draw a horizontal line
void fb_hline(fbpos_t x, fbpos_t y, fbpos_t w, fbcolor_t color);
/// Draw a vertical line
void fb_vline(fbpos_t x, fbpos_t y, fbpos_t h, fbcolor_t color);
/// Draw a filled rect
void fb_rect(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbcolor_t color);
/// Draw a frame (unfilled rect)
void fb_frame(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbpos_t thickness, fbcolor_t color);
#if IS_AVR
/// Draw a bitmap from progmem
void fb_bitmap_P(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, const uint8_t *map, fbcolor_t color);
#else
void fb_bitmap(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, const uint8_t *map, fbcolor_t color);
#endif
/// Draw a circle
void fb_circle(fbpos_t x, fbpos_t y, fbpos_t r, uint8_t thickness, fbcolor_t color);
/// Output the framebuffer array `fb` to the display device.
///
/// The user must implement this
extern void fb_blit(void);
#endif //FRAMEBUFFER_H

@ -0,0 +1,8 @@
#ifndef FRAMEBUFFER_CONFIG_H
#define FRAMEBUFFER_CONFIG_H
/* Tiny framebuffer */
#define FBW 128
#define FBH 64
#endif /* FRAMEBUFFER_CONFIG_H */

@ -0,0 +1,164 @@
#include <stdlib.h>
#include <stdio.h>
#include "framebuffer.h"
#include "font.h"
#include "fb_7seg.h"
#include "fb_text.h"
void main() {
fb_clear();
const uint8_t ryba[14] = {
0b11000011,
0b01100110,
0b01111110,
0b00111100,
0b00011000,
0b00111100,
0b01111110,
0b11111111,
0b11111111,
0b11111011,
0b11111111,
0b01111110,
0b00111100,
0b00011000,
};
#define ZIR_W 12
#define ZIR_H 24
const uint8_t zirafa[(ZIR_H/8) * ZIR_W] = {
// levo
0b10000000,
0b01000000,
0b00100000,
0b00010000,
0b00001000,
0b00000100,
0b00000010,
0b00011001,
0b00011111,
0b11111000,
0b00011111,
0b00001000,
//
0b10000000,
0b01000000,
0b00100000,
0b00010000,
0b00001000,
0b00000100,
0b00000010,
0b00000001,
0b10000000,
0b11111111,
0b00100000,
0b00010000,
//
0b00110000,
0b10001000,
0b11111000,
0b00011000,
0b11111000,
0b10011000,
0b00011000,
0b10011000,
0b11111000,
0b00011111,
0b11111000,
0b10000000,
};
//fb_bitmap(0, pos, 14, 8, ryba, 1);
// fb_bitmap(0, 0, ZIR_W, ZIR_H, zirafa, 1);
// fb_bitmap(ZIR_W+2, 12, ZIR_W, ZIR_H, zirafa, 1);
// fb_bitmap((ZIR_W + 2) * 2, 2, ZIR_W, ZIR_H, zirafa, 1);
fb_frame(0, 0, 140, 40, 2, 1);
// uint8_t x = 3;
// uint8_t w = 7;
// uint8_t h = 10;
// uint8_t th = 1;
// uint8_t sp = 3;
// x += fb_7seg_dig(x, 3, w, h, th, /*val*/ 7, /*color*/ 1) + sp;
// x += fb_7seg_dig(x, 3, w, h, th, /*val*/ 9, /*color*/ 1) + sp;
// x += fb_7seg_period(x, 3, w, h, th, /*color*/ 1) + sp;
// x += fb_7seg_dig(x, 3, w, h, th, /*val*/ 5, /*color*/ 1) + sp;
//fb_circle(60, 15, 10, 1, 1);
// fb_circle(60, 15, 5, 1, 1);
//fb_fill_pattern("\xAA\x55", 2, 0);
//fb_invert();
// fb_text(40, 12, "▶Ahoj→!", FONT_BOLD, 1);
fb_text(15, 15, "MEOW", FONT_DOUBLEH, 1);
fb_text(40, 15, "MEOW", FONT_DOUBLEW, 1);
fb_text(40, 23, "MEOW", FONT_BOLD, 1);
fb_text(5, 5, "Tiny 3x5", FONT_3X5, 1);
fb_text(40, 5, "Tiny 4x5 MEOW", FONT_4X5, 1);
fb_frame(12,12,80,20,1,1);
// fb_text(36, 5, "-123456789.0 CPM°", FONT_TINY, 1);
// struct Utf8Iterator iter;
//// Utf8Iterator_Init_P(&iter, str);
// Utf8Iterator_Init(&iter, str);
//
// struct Utf8Char uchar;
// x = 3;
// while ((uchar = Utf8Iterator_Next(&iter)).uint) {
// const font_bitmap_t *sym = font_getsym(&uchar);
// fb_bitmap_P(x, 15, 5, 8, sym->data, 1);
// x += 7;
// }
//
//uint8_t xx[] = {
// 0x1f, 0x11, 0x11, 0x1f,
// 0x02, 0x05, 0x02, 0x00,
// 0x00, 0x04, 0x04, 0x04,
// 0x0e, 0x11, 0x11, 0x0a,
// 0x1f, 0x05, 0x05, 0x02,
// 0x1f, 0x01, 0x02, 0x1f,
// 0x00, 0x18, 0x18, 0x00,
//};
//
//for(int i=0;i<sizeof(xx);i++) {
// uint8_t v = xx[i];
// uint8_t out = 0;
// for(int i=0;i<8;i++) {
// out |= ((v>>i) & 1) << (7-i);
// }
//
// if(i%4==0) printf("\n");
// printf("0x%02x, ", out);
//}
fb_blit();
}
void fb_blit() {
for(int y = 0; y < FBH; y++) {
for(int x = 0; x < FBW; x++) {
bool b = fb[(y/8)*FBW + x] & (1 << (y%8));
if (b) {
printf("\x1b[1;32m█\x1b[m");
} else {
if (y%8 == 0) {
printf("\x1b[34m.\x1b[m");
} else {
printf(".");
}
}
}
printf("\r\n");
}
printf("\r\n");
}

@ -0,0 +1,18 @@
/**
* Progmem support
*/
#ifndef UFB_PROGMEM_H
#define UFB_PROGMEM_H
// if built for AVR, uncommend the include and comment the fake defines:
// #define IS_AVR 1
// #include <avr/pgmspace.h>
#define IS_AVR 0
#define PROGMEM
#define pgm_read_byte(adr) (*adr)
#define pgm_read_dword(adr) (*adr)
#endif //UFB_PROGMEM_H

@ -0,0 +1,129 @@
#include <stdint.h>
#include "utf8.h"
//
// Created by MightyPork on 2017/08/20.
//
// UTF-8 parser - collects bytes of a code point before writing them
// into a screen cell.
//
const struct Utf8Char EMPTY_CHAR = (struct Utf8Char) {.uint = 0};
// Code Points First Byte Second Byte Third Byte Fourth Byte
// U+0000 - U+007F 00 - 7F
// U+0080 - U+07FF C2 - DF 80 - BF
// U+0800 - U+0FFF E0 *A0 - BF 80 - BF
// U+1000 - U+CFFF E1 - EC 80 - BF 80 - BF
// U+D000 - U+D7FF ED 80 - *9F 80 - BF
// U+E000 - U+FFFF EE - EF 80 - BF 80 - BF
// U+10000 - U+3FFFF F0 *90 - BF 80 - BF 80 - BF
// U+40000 - U+FFFFF F1 - F3 80 - BF 80 - BF 80 - BF
// U+100000 - U+10FFFF F4 80 - *8F 80 - BF 80 - BF
size_t utf8_strlen(const char *text)
{
// TODO optimize
struct Utf8Iterator iter;
Utf8Iterator_Init(&iter, text);
size_t num = 0;
while ((Utf8Iterator_Next(&iter)).uint) {
num++;
}
return num;
}
/**
* Handle a received character
*/
struct Utf8Char Utf8Parser_Handle(struct Utf8Parser *self, char c)
{
uint8_t *bytes = self->buffer.bytes;
uint8_t uc = (uint8_t) c;
// collecting unicode glyphs...
if (uc & 0x80) {
if (self->utf_len == 0) {
bytes[0] = uc;
self->utf_j = 1;
// start
if (uc == 0xC0 || uc == 0xC1 || uc > 0xF4) {
// forbidden start codes
goto fail;
}
if ((uc & 0xE0) == 0xC0) {
self->utf_len = 2;
}
else if ((uc & 0xF0) == 0xE0) {
self->utf_len = 3;
}
else if ((uc & 0xF8) == 0xF0) {
self->utf_len = 4;
}
else {
// chars over 127 that don't start unicode sequences
goto fail;
}
}
else {
if ((uc & 0xC0) != 0x80) {
bytes[self->utf_j++] = uc;
goto fail;
}
else {
bytes[self->utf_j++] = uc;
if (self->utf_j >= self->utf_len) {
// check for bad sequences - overlong or some other problem
if (bytes[0] == 0xF4 && bytes[1] > 0x8F) goto fail;
if (bytes[0] == 0xF0 && bytes[1] < 0x90) goto fail;
if (bytes[0] == 0xED && bytes[1] > 0x9F) goto fail;
if (bytes[0] == 0xE0 && bytes[1] < 0xA0) goto fail;
// trap for surrogates - those break javascript
if (bytes[0] == 0xED && bytes[1] >= 0xA0 && bytes[1] <= 0xBF) goto fail;
goto success;
}
}
}
}
else {
bytes[0] = uc;
goto success;
}
return EMPTY_CHAR;
success:;
struct Utf8Char result = self->buffer;
self->buffer.uint = 0; // erase the buffer
self->utf_len = 0;
return result;
fail:
self->buffer.uint = 0; // erase the buffer
self->utf_len = 0;
return EMPTY_CHAR;
}
struct Utf8Char Utf8Iterator_Next(struct Utf8Iterator *self)
{
char c;
struct Utf8Char uchar;
while (1) {
if (self->is_progmem) {
c = pgm_read_byte(self->source++);
} else {
c = *self->source++;
}
if (!c) break;
uchar = Utf8Parser_Handle(&self->parser, c);
if (uchar.uint) {
return uchar;
}
}
return EMPTY_CHAR;
}

@ -0,0 +1,93 @@
/**
* UTF-8 string parsing and character iteration
*
* Created on 2020/01/04.
*/
#ifndef LIQUIDTYPE_UTF8_H
#define LIQUIDTYPE_UTF8_H
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include "progmem.h"
/**
* UTF-8 encoded character.
*/
struct Utf8Char {
union {
/** character bytes; padded by zero bytes if shorter than 4 */
uint8_t bytes[4];
/** u32 view of the bytes */
uint32_t uint;
};
};
/** UTF8 string parser internal state */
struct Utf8Parser {
/** UTF-8 bytes buffer */
struct Utf8Char buffer;
/** Currently collected UTF-8 character length */
uint8_t utf_len;
/** Position in the current character */
uint8_t utf_j;
};
static inline void Utf8Parser_Clear(struct Utf8Parser *self) {
self->buffer.uint = 0;
self->utf_j = 0;
self->utf_len = 0;
}
/**
* Utf8 character iterator.
*
* Usage:
* struct Utf8Iterator iter;
* Utf8Iterator_Init(&iter, myString);
*
* union Utf8Char uchar;
* while ((uchar = Utf8Iterator_Next(&iter)).uint) {
* // do something with the char
* }
*
* // Free myString if needed, it is not mutated.
*/
struct Utf8Iterator {
/* Characters to parse. The pointer is advanced as the iterator progresses. */
const char *source;
struct Utf8Parser parser;
bool is_progmem;
};
static inline void Utf8Iterator_Init(struct Utf8Iterator *self, const char *source) {
Utf8Parser_Clear(&self->parser);
self->source = source;
self->is_progmem = false;
}
static inline void Utf8Iterator_Init_P(struct Utf8Iterator *self, const char *source) {
Utf8Iterator_Init(self, source);
self->is_progmem = true;
}
size_t utf8_strlen(const char *text);
/**
* Get the next character from the iterator; Returns empty character if there are no more characters to parse.
*
* Invalid characters are skipped.
*/
struct Utf8Char Utf8Iterator_Next(struct Utf8Iterator *self);
/**
* Parse a character.
*
* The returned struct contains NIL (uint == 0) if no character is yet available.
*
* ASCII is passed through, utf-8 is collected and returned in one piece.
*/
struct Utf8Char Utf8Parser_Handle(struct Utf8Parser *self, char c);
#endif //LIQUIDTYPE_UTF8_H
Loading…
Cancel
Save