commit
b4f9b87ce0
@ -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) |
@ -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}) |
@ -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,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…
Reference in new issue