tiny font ver

master
Ondřej Hruška 1 year ago
parent fecadb3821
commit 80d5635e95
  1. 8
      Makefile
  2. 72
      fb_7seg.c
  3. 32
      fb_7seg.h
  4. 78
      fb_text.c
  5. 27
      fb_text.h
  6. 224
      font.c
  7. 29
      font.h
  8. 158
      framebuffer.c
  9. 51
      framebuffer.h
  10. 7
      framebuffer_config.h
  11. 48
      main.c
  12. 14
      progmem.h
  13. 129
      utf8.c
  14. 93
      utf8.h

@ -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

224
font.c

@ -0,0 +1,224 @@
#include "font.h"
#include "utf8.h"
#include "progmem.h"
#include <stdint.h>
#define FONT_EXTRAS_START (0x7e - 0x20 + 1)
struct utf_glyph5x {
union {
const char symbol[4];
/// symbol as uint, but not decoded - just for matching!
const uint32_t uint;
};
font5x_bitmap_t graphic;
};
struct utf_glyph4x {
union {
const char symbol[4];
const uint32_t uint;
};
font4x_bitmap_t graphic;
};
#define F57_NUM_ASCII 95
#define F57_ASCII_START 0x20
#define F57_ASCII_END 0x7e
// ASCII symbols are stored as bare graphic to reduce ROM size
static const font5x_bitmap_t PROGMEM font57_ascii[F57_NUM_ASCII] = {
{{0x00, 0x00, 0x00, 0x00, 0x00}}, // 0x20
{{0x00, 0x00, 0x5f, 0x00, 0x00}}, // ! 0x21
{{0x00, 0x07, 0x00, 0x07, 0x00}}, // \" 0x22
{{0x14, 0x7f, 0x14, 0x7f, 0x14}}, // # 0x23
{{0x24, 0x2a, 0x7f, 0x2a, 0x12}}, // $ 0x24
{{0x23, 0x13, 0x08, 0x64, 0x62}}, // % 0x25
{{0x36, 0x49, 0x55, 0x22, 0x50}}, // & 0x26
{{0x00, 0x05, 0x03, 0x00, 0x00}}, // ' 0x27
{{0x00, 0x1c, 0x22, 0x41, 0x00}}, // ( 0x28
{{0x00, 0x41, 0x22, 0x1c, 0x00}}, // ) 0x29
{{0x14, 0x08, 0x3e, 0x08, 0x14}}, // * 0x2a
{{0x08, 0x08, 0x3e, 0x08, 0x08}}, // + 0x2b
{{0x00, 0x50, 0x30, 0x00, 0x00}}, // , 0x2c
{{0x08, 0x08, 0x08, 0x08, 0x08}}, // - 0x2d
{{0x00, 0x60, 0x60, 0x00, 0x00}}, // . 0x2e
{{0x20, 0x10, 0x08, 0x04, 0x02}}, // / 0x2f
{{0x3e, 0x51, 0x49, 0x45, 0x3e}}, // 0 0x30
{{0x00, 0x42, 0x7f, 0x40, 0x00}}, // 1 0x31
{{0x42, 0x61, 0x51, 0x49, 0x46}}, // 2 0x32
{{0x21, 0x41, 0x45, 0x4b, 0x31}}, // 3 0x33
{{0x18, 0x14, 0x12, 0x7f, 0x10}}, // 4 0x34
{{0x27, 0x45, 0x45, 0x45, 0x39}}, // 5 0x35
{{0x3c, 0x4a, 0x49, 0x49, 0x30}}, // 6 0x36
{{0x01, 0x71, 0x09, 0x05, 0x03}}, // 7 0x37
{{0x36, 0x49, 0x49, 0x49, 0x36}}, // 8 0x38
{{0x06, 0x49, 0x49, 0x29, 0x1e}}, // 9 0x39
{{0x00, 0x36, 0x36, 0x00, 0x00}}, // : 0x3a
{{0x00, 0x56, 0x36, 0x00, 0x00}}, // ; 0x3b
{{0x08, 0x14, 0x22, 0x41, 0x00}}, // < 0x3c
{{0x14, 0x14, 0x14, 0x14, 0x14}}, // = 0x3d
{{0x00, 0x41, 0x22, 0x14, 0x08}}, // > 0x3e
{{0x02, 0x01, 0x51, 0x09, 0x06}}, // ? 0x3f
{{0x32, 0x49, 0x79, 0x41, 0x3e}}, // @ 0x40
{{0x7e, 0x11, 0x11, 0x11, 0x7e}}, // A 0x41
{{0x7f, 0x49, 0x49, 0x49, 0x36}}, // B 0x42
{{0x3e, 0x41, 0x41, 0x41, 0x22}}, // C 0x43
{{0x7f, 0x41, 0x41, 0x22, 0x1c}}, // D 0x44
{{0x7f, 0x49, 0x49, 0x49, 0x41}}, // E 0x45
{{0x7f, 0x09, 0x09, 0x09, 0x01}}, // F 0x46
{{0x3e, 0x41, 0x49, 0x49, 0x7a}}, // G 0x47
{{0x7f, 0x08, 0x08, 0x08, 0x7f}}, // H 0x48
{{0x00, 0x41, 0x7f, 0x41, 0x00}}, // I 0x49
{{0x20, 0x40, 0x41, 0x3f, 0x01}}, // J 0x4a
{{0x7f, 0x08, 0x14, 0x22, 0x41}}, // K 0x4b
{{0x7f, 0x40, 0x40, 0x40, 0x40}}, // L 0x4c
{{0x7f, 0x02, 0x0c, 0x02, 0x7f}}, // M 0x4d
{{0x7f, 0x04, 0x08, 0x10, 0x7f}}, // N 0x4e
{{0x3e, 0x41, 0x41, 0x41, 0x3e}}, // O 0x4f
{{0x7f, 0x09, 0x09, 0x09, 0x06}}, // P 0x50
{{0x3e, 0x41, 0x51, 0x21, 0x5e}}, // Q 0x51
{{0x7f, 0x09, 0x19, 0x29, 0x46}}, // R 0x52
{{0x46, 0x49, 0x49, 0x49, 0x31}}, // S 0x53
{{0x01, 0x01, 0x7f, 0x01, 0x01}}, // T 0x54
{{0x3f, 0x40, 0x40, 0x40, 0x3f}}, // U 0x55
{{0x1f, 0x20, 0x40, 0x20, 0x1f}}, // V 0x56
{{0x3f, 0x40, 0x38, 0x40, 0x3f}}, // W 0x57
{{0x63, 0x14, 0x08, 0x14, 0x63}}, // X 0x58
{{0x07, 0x08, 0x70, 0x08, 0x07}}, // Y 0x59
{{0x61, 0x51, 0x49, 0x45, 0x43}}, // Z 0x5a
{{0x00, 0x7f, 0x41, 0x41, 0x00}}, // [ 0x5b
{{0x02, 0x04, 0x08, 0x10, 0x20}}, // \\ 0x5c
{{0x00, 0x41, 0x41, 0x7f, 0x00}}, // ] 0x5d
{{0x04, 0x02, 0x01, 0x02, 0x04}}, // ^ 0x5e
{{0x40, 0x40, 0x40, 0x40, 0x40}}, // _ 0x5f
{{0x00, 0x01, 0x02, 0x04, 0x00}}, // ` 0x60
{{0x20, 0x54, 0x54, 0x54, 0x78}}, // a 0x61
{{0x7f, 0x48, 0x44, 0x44, 0x38}}, // b 0x62
{{0x38, 0x44, 0x44, 0x44, 0x20}}, // c 0x63
{{0x38, 0x44, 0x44, 0x48, 0x7f}}, // d 0x64
{{0x38, 0x54, 0x54, 0x54, 0x18}}, // e 0x65
{{0x08, 0x7e, 0x09, 0x01, 0x02}}, // f 0x66
{{0x0c, 0x52, 0x52, 0x52, 0x3e}}, // g 0x67
{{0x7f, 0x08, 0x04, 0x04, 0x78}}, // h 0x68
{{0x00, 0x44, 0x7d, 0x40, 0x00}}, // i 0x69
{{0x20, 0x40, 0x44, 0x3d, 0x00}}, // j 0x6a
{{0x7f, 0x10, 0x28, 0x44, 0x00}}, // k 0x6b
{{0x00, 0x41, 0x7f, 0x40, 0x00}}, // l 0x6c
{{0x7c, 0x04, 0x18, 0x04, 0x78}}, // m 0x6d
{{0x7c, 0x08, 0x04, 0x04, 0x78}}, // n 0x6e
{{0x38, 0x44, 0x44, 0x44, 0x38}}, // o 0x6f
{{0x7c, 0x14, 0x14, 0x14, 0x08}}, // p 0x70
{{0x08, 0x14, 0x14, 0x18, 0x7c}}, // q 0x71
{{0x7c, 0x08, 0x04, 0x04, 0x08}}, // r 0x72
{{0x48, 0x54, 0x54, 0x54, 0x20}}, // s 0x73
{{0x04, 0x3f, 0x44, 0x40, 0x20}}, // t 0x74
{{0x3c, 0x40, 0x40, 0x20, 0x7c}}, // u 0x75
{{0x1c, 0x20, 0x40, 0x20, 0x1c}}, // v 0x76
{{0x3c, 0x40, 0x30, 0x40, 0x3c}}, // w 0x77
{{0x44, 0x28, 0x10, 0x28, 0x44}}, // x 0x78
{{0x0c, 0x50, 0x50, 0x50, 0x3c}}, // y 0x79
{{0x44, 0x64, 0x54, 0x4c, 0x44}}, // z 0x7a
{{0x00, 0x08, 0x36, 0x41, 0x00}}, // { 0x7b
{{0x00, 0x00, 0x7f, 0x00, 0x00}}, // | 0x7c
{{0x00, 0x41, 0x36, 0x08, 0x00}}, // } 0x7d
{{0x10, 0x08, 0x08, 0x10, 0x08}}, // ~ 0x7e
};
#define F57_NUM_EXTRA 16
// utf8 characters list ending with empty struct
static const struct utf_glyph5x PROGMEM font57_extra[F57_NUM_EXTRA] = {
{.symbol="<EFBFBD>", {{0xFE, 0x82, 0x82, 0x82, 0xFE}}}, // box - error code
{.symbol="×", {{0x22, 0x14, 0x08, 0x14, 0x22}}}, // cross
{.symbol="", {{0x08, 0x04, 0x3e, 0x04, 0x08}}}, // arrow_up
{.symbol="", {{0x08, 0x10, 0x3e, 0x10, 0x08}}}, // arrow_down
{.symbol="", {{0x08, 0x1c, 0x2a, 0x08, 0x08}}}, // arrow_left
{.symbol="", {{0x08, 0x08, 0x2a, 0x1c, 0x08}}}, // arrow_right
{.symbol="", {{0x1c, 0x22, 0x2e, 0x2a, 0x1c}}}, // clock
{.symbol="", {{0x63, 0x55, 0x4d, 0x55, 0x63}}}, // hourglass
{.symbol="", {{0x1c, 0x22, 0x2a, 0x22, 0x1c}}}, // wheel
{.symbol="", {{0x10, 0x38, 0x54, 0x10, 0x1e}}}, // return
{.symbol="🌡", {{0x60, 0x9e, 0x81, 0x9e, 0x6a}}}, // thermometer
{.symbol="°", {{0x00, 0x07, 0x05, 0x07, 0x00}}}, // degree
{.symbol="μ", {{0x7C, 0x20, 0x20, 0x10, 0x3C}}}, // micro
{.symbol="🔙", {{0x04, 0x4e, 0x55, 0x44, 0x38}}}, // back
{.symbol="", {{0x7f, 0x3e, 0x1c, 0x08, 0x00}}}, // tri_right
{.symbol="", {{0x00, 0x08, 0x1c, 0x3e, 0x7f}}}, // tri_left
};
#define F45_NUM_ASCII
static const font4x_bitmap_t PROGMEM font45_ascii[F45_NUM_ASCII] = {
{{0x0e, 0x11, 0x11, 0x0e}}, // 0
{{0x04, 0x12, 0x1f, 0x10}}, // 1
{{0x12, 0x19, 0x15, 0x12}}, // 2
{{0x0a, 0x11, 0x15, 0x0a}}, // 3
{{0x07, 0x04, 0x1e, 0x04}}, // 4
{{0x17, 0x15, 0x15, 0x09}}, // 5
{{0x0e, 0x15, 0x15, 0x09}}, // 6
{{0x11, 0x09, 0x05, 0x03}}, // 7
{{0x0a, 0x15, 0x15, 0x0a}}, // 8
{{0x12, 0x15, 0x15, 0x0a}}, // 9
{{0x00, 0x00, 0x00, 0x00}}, // ' '
};
#define F45_NUM_EXTRA 7
static const struct utf_glyph4x PROGMEM font45_extra[F45_NUM_EXTRA] = {
{.symbol="<EFBFBD>", {{0x1f, 0x11, 0x11, 0x1f}}}, // box - error code
{.symbol="°", {{0x02, 0x05, 0x02, 0x00}}},
{.symbol="-", {{0x00, 0x04, 0x04, 0x04}}},
{.symbol="C", {{0x0e, 0x11, 0x11, 0x0a}}},
{.symbol="P", {{0x1f, 0x05, 0x05, 0x02}}},
{.symbol="M", {{0x1f, 0x01, 0x02, 0x1f}}},
{.symbol=".", {{0x00, 0x18, 0x18, 0x00}}},
};
const font5x_bitmap_t *font57_getsym(const struct Utf8Char *ch)
{
const uint8_t byte0 = ch->bytes[0];
if (byte0 < F57_ASCII_START) {
// low ASCII is not supported.
goto fail;
}
if (byte0 <= F57_ASCII_END) {
// valid ASCII at hard positions.
// In this case only the first byte is significant.
return &font57_ascii[byte0 - F57_ASCII_START];
}
// search UTF8
for (uint8_t i = 0; i < F57_NUM_EXTRA; 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
}
const font4x_bitmap_t *font45_getsym(const struct Utf8Char *ch)
{
const uint8_t byte0 = ch->bytes[0];
if (byte0 == ' ') {
return &font45_ascii[10];
}
if (byte0 >= '0' && byte0 <= '9') {
return &font45_ascii[byte0 - '0'];
}
// search UTF8
for (uint8_t i = 0; i < F45_NUM_EXTRA; i++) {
const struct utf_glyph4x *sym = &font45_extra[i];
const uint32_t table_ch = pgm_read_dword(&sym->uint);
if (table_ch == ch->uint) {
return &sym->graphic;
}
}
fail:
return &font45_extra[0].graphic; // replacement character
}

@ -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

@ -3,13 +3,14 @@
// //
#include "framebuffer.h" #include "framebuffer.h"
#include "progmem.h"
#define MIN(a, b) ((a)>(b)?(b):(a)) #define MIN(a, b) ((a)>(b)?(b):(a))
#define MAX(a, b) ((a)>(b)?(a):(b)) #define MAX(a, b) ((a)>(b)?(a):(b))
#include <string.h> #include <string.h>
uint8_t fb[(FBH / 8) * FBW]; uint8_t fb[FB_LEN];
/** Fill with a vertical pattern, 1 byte */ /** Fill with a vertical pattern, 1 byte */
void fb_fill(uint8_t pattern) void fb_fill(uint8_t pattern)
@ -17,7 +18,7 @@ void fb_fill(uint8_t pattern)
memset(fb, pattern, sizeof(fb)); memset(fb, pattern, sizeof(fb));
} }
static void draw_mask(fbsize_t idx, uint8_t mask, uint8_t color) static void draw_mask(fbsize_t idx, uint8_t mask, fbcolor_t color)
{ {
if (color != 0) { if (color != 0) {
fb[idx] |= mask; fb[idx] |= mask;
@ -26,7 +27,26 @@ static void draw_mask(fbsize_t idx, uint8_t mask, uint8_t color)
} }
} }
void fb_px(fbpos_t x, fbpos_t y, uint8_t color) 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; } if (x >= FBW || y >= FBH) { return; }
const fbpos_t row = y / 8; const fbpos_t row = y / 8;
@ -48,7 +68,7 @@ uint8_t fb_getpx(fbpos_t x, fbpos_t y)
} }
} }
void fb_hline(fbpos_t x, fbpos_t y, fbpos_t w, uint8_t color) void fb_hline(fbpos_t x, fbpos_t y, fbpos_t w, fbcolor_t color)
{ {
if (x >= FBW || y >= FBH) { return; } if (x >= FBW || y >= FBH) { return; }
w = MIN(FBW - x, w); w = MIN(FBW - x, w);
@ -61,7 +81,7 @@ void fb_hline(fbpos_t x, fbpos_t y, fbpos_t w, uint8_t color)
} }
} }
void fb_vline(fbpos_t x, fbpos_t y, fbpos_t h, uint8_t color) void fb_vline(fbpos_t x, fbpos_t y, fbpos_t h, fbcolor_t color)
{ {
if (x >= FBW || y >= FBH) { return; } if (x >= FBW || y >= FBH) { return; }
h = MIN(FBH - y - 1, h); h = MIN(FBH - y - 1, h);
@ -94,7 +114,7 @@ void fb_vline(fbpos_t x, fbpos_t y, fbpos_t h, uint8_t color)
} }
} }
void fb_rect(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, uint8_t 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; } if (x >= FBW || y >= FBH) { return; }
w = MIN(FBW - x, w); w = MIN(FBW - x, w);
@ -146,7 +166,7 @@ void fb_rect(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, uint8_t color)
} }
} }
void fb_frame(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbpos_t thickness, uint8_t 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) { if (thickness == 0) {
return; return;
@ -163,7 +183,53 @@ void fb_frame(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbpos_t thickness, uin
} }
} }
void fb_bitmap(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, const uint8_t *map, uint8_t 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--;
}
}
void fb_bitmap_P(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, const uint8_t *map, fbcolor_t color)
{ {
if (x >= FBW || y >= FBH) { return; } if (x >= FBW || y >= FBH) { return; }
const fbpos_t w0 = w; const fbpos_t w0 = w;
@ -176,7 +242,7 @@ void fb_bitmap(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, const uint8_t *map, u
if (rowrem + h <= 8) { if (rowrem + h <= 8) {
for (fbpos_t i = 0; i < w; i++) { for (fbpos_t i = 0; i < w; i++) {
// all within one cell // all within one cell
const uint8_t mask = (map[i] & (0xFF >> (8 - h))) << rowrem; const uint8_t mask = (pgm_read_byte(&map[i]) & (0xFF >> (8 - h))) << rowrem;
draw_mask(cell + i, mask, color); draw_mask(cell + i, mask, color);
} }
return; return;
@ -187,7 +253,7 @@ void fb_bitmap(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, const uint8_t *map, u
// This could be optimized to walk each row of the canvas only once, but the code would get bigger. // This could be optimized to walk each row of the canvas only once, but the code would get bigger.
while (h > 0) { while (h > 0) {
for (fbpos_t i = 0; i < w; i++) { for (fbpos_t i = 0; i < w; i++) {
const uint8_t mask = (map[i + mapc0] & (0xFF >> rowrem)) << rowrem; const uint8_t mask = (pgm_read_byte(&map[i + mapc0]) & (0xFF >> rowrem)) << rowrem;
draw_mask(cell + i, mask, color); draw_mask(cell + i, mask, color);
} }
@ -195,7 +261,7 @@ void fb_bitmap(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, const uint8_t *map, u
if (rowrem != 0) { if (rowrem != 0) {
for (fbpos_t i = 0; i < w; i++) { for (fbpos_t i = 0; i < w; i++) {
const uint8_t mask = (map[i + mapc0] & (0xFF << (8 - rowrem))) >> (8 - rowrem); const uint8_t mask = (pgm_read_byte(&map[i + mapc0]) & (0xFF << (8 - rowrem))) >> (8 - rowrem);
draw_mask(cell + i, mask, color); draw_mask(cell + i, mask, color);
} }
} }
@ -210,73 +276,3 @@ void fb_bitmap(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, const uint8_t *map, u
} }
} }
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, uint8_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, uint8_t color)
{
const fbpos_t hi = (h - th * 3) / 2;
fb_rect(x, y + hi * 2 + th * 2, th, th, color);
return th;
}

@ -15,6 +15,9 @@
typedef uint16_t fbsize_t; typedef uint16_t fbsize_t;
typedef uint8_t fbpos_t; typedef uint8_t fbpos_t;
typedef uint8_t fbcolor_t;
#define FB_LEN ((FBH / 8) * FBW)
/// Framebuffer backing array. /// Framebuffer backing array.
/// ///
@ -27,60 +30,50 @@ typedef uint8_t fbpos_t;
/// a7 b7 /// a7 b7
/// ///
/// and more bytes continue rows 8-15 and so on /// and more bytes continue rows 8-15 and so on
extern uint8_t fb[(FBH / 8) * FBW]; extern uint8_t fb[ FB_LEN ];
/// Fill the entire screen with a byte pattern. /// 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. /// Use 0xFF or 0x00 for a solid fill. Other patterns may be used to create horizontal stripes.
void fb_fill(uint8_t pattern); void fb_fill(uint8_t pattern);
/// Invert the entire screen
void fb_invert();
/// Clear the display (fill with 0x00) /// Clear the display (fill with 0x00)
static inline void fb_clear(void) static inline void fb_clear(void)
{ {
fb_fill(0); 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 /// Set a single pixel
void fb_px(fbpos_t x, fbpos_t y, uint8_t color); void fb_px(fbpos_t x, fbpos_t y, fbcolor_t color);
/// Get pixel color /// Get pixel color
uint8_t fb_getpx(fbpos_t x, fbpos_t y); uint8_t fb_getpx(fbpos_t x, fbpos_t y);
/// Draw a horizontal line /// Draw a horizontal line
void fb_hline(fbpos_t x, fbpos_t y, fbpos_t w, uint8_t color); void fb_hline(fbpos_t x, fbpos_t y, fbpos_t w, fbcolor_t color);
/// Draw a vertical line /// Draw a vertical line
void fb_vline(fbpos_t x, fbpos_t y, fbpos_t h, uint8_t color); void fb_vline(fbpos_t x, fbpos_t y, fbpos_t h, fbcolor_t color);
/// Draw a filled rect /// Draw a filled rect
void fb_rect(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, uint8_t color); void fb_rect(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbcolor_t color);
/// Draw a frame (unfilled rect) /// Draw a frame (unfilled rect)
void fb_frame(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbpos_t thickness, uint8_t color); void fb_frame(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbpos_t thickness, fbcolor_t color);
/// Draw a bitmap /// Draw a bitmap from progmem
void fb_bitmap(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, const uint8_t *map, uint8_t color); void fb_bitmap_P(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, const uint8_t *map, fbcolor_t color);
/// Draw a 7-segment digit. Returns its width (without spacing) /// Draw a circle
/// void fb_circle(fbpos_t x, fbpos_t y, fbpos_t r, uint8_t thickness, fbcolor_t color);
/// \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, uint8_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, uint8_t color);
/// Output the framebuffer array `fb` to the display device. /// Output the framebuffer array `fb` to the display device.
/// ///

@ -2,12 +2,7 @@
#define FRAMEBUFFER_CONFIG_H #define FRAMEBUFFER_CONFIG_H
/* Tiny framebuffer */ /* Tiny framebuffer */
#define FBW 48 #define FBW 128
#define FBH 32 #define FBH 32
// if built for AVR, uncommend the include and comment the fake defines:
// #include <avr/pgmspace.h>
#define PROGMEM
#define pgm_read_byte(adr) (*adr)
#endif /* FRAMEBUFFER_CONFIG_H */ #endif /* FRAMEBUFFER_CONFIG_H */

@ -1,6 +1,9 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include "framebuffer.h" #include "framebuffer.h"
#include "font.h"
#include "fb_7seg.h"
#include "fb_text.h"
void main() { void main() {
fb_clear(); fb_clear();
@ -72,7 +75,7 @@ void main() {
// fb_bitmap(ZIR_W+2, 12, 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_bitmap((ZIR_W + 2) * 2, 2, ZIR_W, ZIR_H, zirafa, 1);
fb_frame(0, 0, 48, 32, 2, 1); fb_frame(0, 0, 128, 32, 2, 1);
uint8_t x = 3; uint8_t x = 3;
uint8_t w = 7; uint8_t w = 7;
@ -84,7 +87,48 @@ void main() {
x += fb_7seg_period(x, 3, w, h, th, /*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; x += fb_7seg_dig(x, 3, w, h, th, /*val*/ 5, /*color*/ 1) + sp;
//fb_frame(10, 7, 20, 20, 2, 1); //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(3, 15, "Ahoj→! 43.15 μSv/h", 0, 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(); fb_blit();
} }

@ -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

129
utf8.c

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

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