commit b4f9b87ce08d1725660d46e2c5036b475f74787c Author: Ondřej Hruška Date: Wed Dec 28 20:04:28 2022 +0100 add files diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fc9a054 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build +cmake-build-* +.idea/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..699156b --- /dev/null +++ b/CMakeLists.txt @@ -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) diff --git a/main.c b/main.c new file mode 100644 index 0000000..b785904 --- /dev/null +++ b/main.c @@ -0,0 +1,276 @@ +#include +#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(); +} diff --git a/pico_sdk_import.cmake b/pico_sdk_import.cmake new file mode 100644 index 0000000..65f8a6f --- /dev/null +++ b/pico_sdk_import.cmake @@ -0,0 +1,73 @@ +# This is a copy of /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}) diff --git a/ufb/.gitignore b/ufb/.gitignore new file mode 100644 index 0000000..8477f92 --- /dev/null +++ b/ufb/.gitignore @@ -0,0 +1,4 @@ +*.o +ufb-test +.idea/ + diff --git a/ufb/Makefile b/ufb/Makefile new file mode 100644 index 0000000..56f607e --- /dev/null +++ b/ufb/Makefile @@ -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. diff --git a/ufb/fb_7seg.c b/ufb/fb_7seg.c new file mode 100644 index 0000000..d14003d --- /dev/null +++ b/ufb/fb_7seg.c @@ -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; +} diff --git a/ufb/fb_7seg.h b/ufb/fb_7seg.h new file mode 100644 index 0000000..e4ffe19 --- /dev/null +++ b/ufb/fb_7seg.h @@ -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 diff --git a/ufb/fb_text.c b/ufb/fb_text.c new file mode 100644 index 0000000..8b5da7b --- /dev/null +++ b/ufb/fb_text.c @@ -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); +} diff --git a/ufb/fb_text.h b/ufb/fb_text.h new file mode 100644 index 0000000..b05e900 --- /dev/null +++ b/ufb/fb_text.h @@ -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 diff --git a/ufb/font.c b/ufb/font.c new file mode 100644 index 0000000..e21766c --- /dev/null +++ b/ufb/font.c @@ -0,0 +1,81 @@ +#include "font.h" +#include "utf8.h" +#include "progmem.h" +#include + +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 diff --git a/ufb/font.h b/ufb/font.h new file mode 100644 index 0000000..2631414 --- /dev/null +++ b/ufb/font.h @@ -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 + +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 diff --git a/ufb/font_35.inc.c b/ufb/font_35.inc.c new file mode 100644 index 0000000..6e48af8 --- /dev/null +++ b/ufb/font_35.inc.c @@ -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="�"}, {{0x1f, 0x1f, 0x1f}}}, + {.utf={.symbol="°"}, {{0x07, 0x05, 0x07}}}, +}; diff --git a/ufb/font_45.inc.c b/ufb/font_45.inc.c new file mode 100644 index 0000000..8db4f48 --- /dev/null +++ b/ufb/font_45.inc.c @@ -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="�"}, {{0x1f, 0x11, 0x11, 0x1f}}}, + {.utf={.symbol="°"}, {{0x07, 0x05, 0x07, 0x00}}}, +}; diff --git a/ufb/font_57.inc.c b/ufb/font_57.inc.c new file mode 100644 index 0000000..79f9ea2 --- /dev/null +++ b/ufb/font_57.inc.c @@ -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="�"}, {{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}}}, +}; diff --git a/ufb/fontedit_35.c b/ufb/fontedit_35.c new file mode 100644 index 0000000..df3f297 --- /dev/null +++ b/ufb/fontedit_35.c @@ -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[] = { + "�", + "°", +}; + +#include "fontedit_render.inc.c" diff --git a/ufb/fontedit_45.c b/ufb/fontedit_45.c new file mode 100644 index 0000000..e4402bf --- /dev/null +++ b/ufb/fontedit_45.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[] = { + "�", + "°", +}; + + +#include "fontedit_render.inc.c" diff --git a/ufb/fontedit_57.c b/ufb/fontedit_57.c new file mode 100644 index 0000000..4742f39 --- /dev/null +++ b/ufb/fontedit_57.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[] = { + "�", + "×", + "↑", + "↓", + "←", + "→", + "⏰", // clock + "⌛", + "☸", + "⏎", + "🌡", + "°", + "μ", + "🔙", // back + "▶", + "◀", +}; + +#include "fontedit_render.inc.c" diff --git a/ufb/fontedit_gen_table.c b/ufb/fontedit_gen_table.c new file mode 100644 index 0000000..c8f80b8 --- /dev/null +++ b/ufb/fontedit_gen_table.c @@ -0,0 +1,14 @@ +#include +#include +#include + +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"); +} diff --git a/ufb/fontedit_render.inc.c b/ufb/fontedit_render.inc.c new file mode 100644 index 0000000..bf911fd --- /dev/null +++ b/ufb/fontedit_render.inc.c @@ -0,0 +1,48 @@ +#include +#include +#include + +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(); +} diff --git a/ufb/framebuffer.c b/ufb/framebuffer.c new file mode 100644 index 0000000..0b938bd --- /dev/null +++ b/ufb/framebuffer.c @@ -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 + +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; + } + } +} + diff --git a/ufb/framebuffer.h b/ufb/framebuffer.h new file mode 100644 index 0000000..c0c7c35 --- /dev/null +++ b/ufb/framebuffer.h @@ -0,0 +1,87 @@ +// +// Created by MightyPork on 2022/11/12. +// + +#ifndef FRAMEBUFFER_H +#define FRAMEBUFFER_H + +#include +#include + +#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 diff --git a/ufb/framebuffer_config.h b/ufb/framebuffer_config.h new file mode 100644 index 0000000..1395310 --- /dev/null +++ b/ufb/framebuffer_config.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 */ diff --git a/ufb/main.c b/ufb/main.c new file mode 100644 index 0000000..8c3bd52 --- /dev/null +++ b/ufb/main.c @@ -0,0 +1,164 @@ +#include +#include +#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>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"); +} diff --git a/ufb/progmem.h b/ufb/progmem.h new file mode 100644 index 0000000..116db4f --- /dev/null +++ b/ufb/progmem.h @@ -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 + +#define IS_AVR 0 +#define PROGMEM +#define pgm_read_byte(adr) (*adr) +#define pgm_read_dword(adr) (*adr) + +#endif //UFB_PROGMEM_H diff --git a/ufb/utf8.c b/ufb/utf8.c new file mode 100644 index 0000000..f41f196 --- /dev/null +++ b/ufb/utf8.c @@ -0,0 +1,129 @@ +#include +#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; +} diff --git a/ufb/utf8.h b/ufb/utf8.h new file mode 100644 index 0000000..b85bdb1 --- /dev/null +++ b/ufb/utf8.h @@ -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 +#include +#include +#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