parent
fecadb3821
commit
80d5635e95
@ -1,12 +1,12 @@ |
|||||||
.PHONY: build run |
.PHONY: build run |
||||||
|
|
||||||
build: ufb-test |
all: ufb-test |
||||||
|
|
||||||
run: build |
run: ufb-test |
||||||
./ufb-test
|
./ufb-test
|
||||||
|
|
||||||
clean: |
clean: |
||||||
rm -f ufb-test
|
rm -f ufb-test
|
||||||
|
|
||||||
ufb-test: main.c framebuffer.c framebuffer.h framebuffer_config.h |
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.
|
$(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,78 @@ |
|||||||
|
/**
|
||||||
|
* 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_TINY) { |
||||||
|
symw = 4; |
||||||
|
symh = 5; |
||||||
|
} else { |
||||||
|
symw = 5; |
||||||
|
symh = 7; |
||||||
|
} |
||||||
|
|
||||||
|
while ((uchar = Utf8Iterator_Next(&iter)).uint) { |
||||||
|
const uint8_t *data; |
||||||
|
if (flags & FONT_TINY) { |
||||||
|
const font4x_bitmap_t *sym = font45_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
|
||||||
|
fb_bitmap_P(x, y, symw, symh, data, color); |
||||||
|
if (flags & FONT_BOLD) { |
||||||
|
x++; |
||||||
|
fb_bitmap_P(x, y, symw, symh, data, color); |
||||||
|
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,27 @@ |
|||||||
|
/**
|
||||||
|
* 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_TINY 8 |
||||||
|
/// 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,29 @@ |
|||||||
|
/**
|
||||||
|
* 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; |
||||||
|
|
||||||
|
/// 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); |
||||||
|
|
||||||
|
#endif //GFX_FONT_H
|
@ -0,0 +1,14 @@ |
|||||||
|
/**
|
||||||
|
* Progmem support |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef UFB_PROGMEM_H |
||||||
|
#define UFB_PROGMEM_H |
||||||
|
|
||||||
|
// if built for AVR, uncommend the include and comment the fake defines:
|
||||||
|
// #include <avr/pgmspace.h>
|
||||||
|
#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