Мне как-то mbr подсказал программку "lcd image converter" (в ответ на мое описание веб-морды велосипеда для генерирования шрифтов). Пока вожусь со светодиодной панелью, мне понадобилось сделать набор из нескольких шрифтов для цифр разного размера, вот я и вспомнил про эту штуку. Она достаточно хорошо преобразует в растр разные шрифты, а дальше остается лишь набить вручную все в файле с исходниками.
Саму идею (как и основной шрифт величиной 12 пикселей) я стащил где-то на просторах интернета (Nadyrshin Ruslan, youtube). Но там подход был "в лоб": надо 256 символов — значит, столько места и займем (хоть половина пустая). Меняем подход.
Итак, нужно нам не так уж и много. Сначала заведем заголовочный файл fonts.h, где определим перечень имеющихся шрифтов, базовые функции и структуру, характеризующую шрифт:
// type for font choosing
typedef enum{
FONT_T_MIN = -1, // no fonts <= this
FONT14, // 16x16, font height near 14px
FONTN16, // numbers 16x8, font height 16px
FONTN10, // numbers 10x8, font height 10px
FONT_T_MAX // no fonts >= this
} font_t;
int choose_font(font_t newfont);
const uint8_t *font_char(uint8_t Char);
typedef struct{
const uint8_t *font; // font inself
const uint8_t *enctable; // font encoding table
uint8_t height; // full font matrix height
uint8_t bytes; // amount of bytes in font matrix
uint8_t baseline; // baseline position (coordinate from bottom line)
} afont;
extern const afont *curfont;
Глобальная постоянная curfont является указателем на текущий используемый шрифт (задается он командой choose_font, далее описываю содержимое файла fonts.c):
int choose_font(font_t newfont){
if(newfont >= FONT_T_MAX || newfont <= FONT_T_MIN) return 1;
curfont = &FONTS[newfont];
return 0;
}
Если шрифт не найден, возвращается единица. Если все ОК — нуль.
Функция font_char позволяет получить доступ к нужному символу:
const uint8_t *font_char(uint8_t Char){
uint8_t idx = curfont->enctable[Char];
if(!idx) return NULL; // no this character in font
return &(curfont->font[idx*(curfont->bytes+1)]);
}
Если символ не найден, возвращается NULL. Если найден — указатель на него (первым байтом идет ширина символа в пикселях, далее — данные самого символа).
Еще в файл fonts.c нужно поместить массив — все существующие шрифты:
static const afont FONTS[] = {
[FONT14] = {font14_table, font14_encoding, FONT14HEIGHT, FONT14BYTES, FONT14BASELINE},
[FONTN16] = {fontNumb16_table, fontNumb16_encoding, FONTNUMB16HEIGHT, FONTNUMB16BYTES, FONTNUMB16BASELINE},
[FONTN10] = {fontNumb10_table, fontNumb10_encoding, FONTNUMB10HEIGHT, FONTNUMB10BYTES, FONTNUMB10BASELINE},
};
const afont *curfont = &FONTS[FONT14];
(ну и сам указатель на текущий шрифт).
Но в самом начале этого файла надо перечислить дефайны для удобства рисования. Я не буду все эти 256 строк включать, а только приведу скрипт, генерирующий их:
#!/bin/bash
function bits(){
Ans=""
for x in $(seq 7 -1 0); do
B=$((1<<$x))
if [ $(($1&$B)) -ne 0 ]; then Ans="${Ans}X"
else Ans="${Ans}_"
fi
done
echo $Ans
}
for x in $(seq 0 255); do
printf "#define $(bits $x)\t0x%02x\n" $x
done
Сразу после этого включаем все файлы со шрифтами:
#include "font14.h"
#include "fontNumb16.h"
#include "fontNumb10.h"
Ну а потом уже пишем то, что я выше привел.
Содержимое каждого файла со шрифтами несложно нарисовать. Во-первых, это нужные дефайны:
#define FONTNUMB10BYTES 10
#define FONTNUMB10HEIGHT 10
#define FONTNUMB10BASELINE 0
Во-вторых, таблица для перекодировки (своя для каждого шрифта):
const uint8_t fontNumb10_encoding[256] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0..31
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, // 47
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 0, // 63
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 79
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 95
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 111
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 127
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 143
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 159
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 175
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 191
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 207
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 223
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 239
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 255
};
Здесь определены маленькие цифры (10 пикселей высотой), поэтому почти вся таблица — нули. Зато очень приличная экономия памяти получается.
Сам этот шрифт — в константе fontNumb10_table[]. Первым ее элементом обязательно должен идти символ-пустышка с нулевой шириной, а дальше — определяемые символы. Скажем, для этого шрифта начало данного массива имеет вид
const uint8_t fontNumb10_table[] = {
// 0x00 - empty
0,
________,
________,
________,
________,
________,
________,
________,
________,
________,
________,
// 0x20 - ' '
4,
________,
________,
________,
________,
________,
________,
________,
________,
________,
________,
// 0x30 - '0'
7,
__XX____,
_X__X___,
X____X__,
X____X__,
X____X__,
X____X__,
X____X__,
X____X__,
_X__X___,
__XX____,
Руслан Надыршин подсказал превосходнейший способ визуализации данных! Мы просто определяем 256 макросов для каждого числа, вместо нуля симпол подчеркивания, вместо единицы — X. И можно прямо в исходниках рисовать символы.
Другой шрифт с цифрами имеет высоту 16 пикселей — на всю высоту экрана. Поэтому там символы выглядят как-то так:
// 0x32 - '2'
7,
__XX____,
_XXXX___,
XX_XXX__,
X___XX__,
____XX__,
____XX__,
____XX__,
____XX__,
___XX___,
___X____,
__XX____,
__X_____,
_XX_____,
XX______,
XX___X__,
XXXXXX__,
А в оригинальном шрифте буквы занимают по высоте почти все 16 пикселей (у некоторых вниз выползает хвостик, у некоторых — вверх). И выглядят они уже вот так:
// 0x40
,
16,
_____XXX,XXX_____,
___XXXXX,XXXXX___,
__XXX___,___XXX__,
_XXX__XX,X_XXXX__,
_XX_XXXX,XXXX_XX_,
XXX_XX__,_XXX_XX_,
XX_XX___,_XX__XX_,
XX_XX___,_XX__XX_,
XX_XX___,_XX__XX_,
XX_XX___,XXX_XX__,
XX_XXXXX,XXXXX___,
_XX_XXXX,_XXX____,
_XXX____,_____XX_,
__XXX___,___XXX__,
___XXXXX,XXXXX___,
_____XXX,XXX_____
Здесь, правда, приходится разделять байты запятыми, что немножко ломает наглядность. Но определять 65536 макросов что-то не хочется: боюсь, что сборка в этом случае затянется...
Вот на таких широченных шрифтах 16х16 пикселей экономия флеша при индексации получается вполне приличной: ведь один символ занимает 33 байта, а таблица с индексами — 256. Если в шрифте не определено 8 символов, индексация уже экономит немного места. А если там определено лишь полтора десятка, так совсем прилично!
Journal information