Я использую ЖК-дисплей 3.5: TFT с Arduino Uno и библиотекой от производителя, библиотекой KeDei TFT. Библиотека поставлялась с таблицей растровых шрифтов, которая огромна для небольшого объема памяти Arduino Uno, поэтому я искал альтернативы.
Я столкнулся с тем, что, похоже, не существует стандартного представления, и некоторые таблицы растровых шрифтов, которые я обнаружил, работают нормально, а другие отображаются как странные каракули и метки, или отображаются вверх ногами, или отображаются с перевернутыми буквами. Написав простое приложение для отображения некоторых символов, я наконец понял, что разные растровые изображения используют разную ориентацию символов.
Мой вопрос
Каковы правила, стандарты или ожидаемые представления битовых данных для растровых шрифтов? Почему кажется, что с растровыми шрифтами используется несколько разных ориентаций текстовых символов?
Мысли о вопросе
Это связано с разными целевыми устройствами, такими как драйвер дисплея Windows или драйвер дисплея Linux, по сравнению с драйвером дисплея Arduino TFT LCD с «голым металлом»?
Какие критерии используются для определения конкретного представления растрового шрифта в виде серии значений без знака char? Имеют ли различные типы растровых устройств, такие как ЖК-дисплей TFT и его контроллер, различную последовательность битов при рисовании на поверхности дисплея путем установки цветов пикселей?
Какие другие возможные представления растровых шрифтов, требующие преобразования, которые моя версия библиотеки в настоящее время не предлагает, существуют?
Есть ли какой-то другой метод, отличный от подхода, который я использую, чтобы определить, какое преобразование необходимо? В настоящее время я подключаю таблицу растровых шрифтов к тестовой программе и распечатываю набор символов, чтобы посмотреть, как это выглядит, а затем точно настраиваю преобразование, тестируя с помощью Arduino и ЖК-экрана TFT.
Мой опыт до сих пор
Библиотека KeDei TFT поставлялась с таблицей растровых шрифтов, которая была определена как
const unsigned char font_table_16_col[96][16] = {
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },/*" ",0*/
{ 0x00,0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x38,0x38,0x00,0x00 },/*"!",1*/
{ 0x00,0xD8,0xFC,0x6C,0x36,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },/*""",2*/
{ 0x00,0x00,0x00,0x6C,0x6C,0x6C,0xFF,0x36,0x36,0x36,0xFF,0x36,0x36,0x36,0x00,0x00 },/*"#",3*/
{ 0x00,0x00,0x18,0x3C,0x7E,0x7E,0x1E,0x1C,0x38,0x78,0x78,0x7E,0x7E,0x3C,0x18,0x18 },/*"$",4*/
{ 0x00,0x00,0x00,0x66,0x6F,0x3F,0x3F,0x3F,0x7E,0xF8,0xFC,0xFC,0xFC,0x66,0x00,0x00 },/*"%",5*/
...
Я не полностью знаком со стандартными описаниями растровых шрифтов, однако я думаю об этом как о растровом шрифте 8x16, в котором каждый символ имеет ширину 8 пикселей и высоту 16 пикселей, или растровый шрифт 8x16.
Из-за размера этой таблицы и небольшого объема памяти Arduino Uno я начал искать другие растровые шрифты, которые были бы разборчивыми и при этом занимали бы меньше памяти. См. уменьшение память, необходимая для библиотеки KeDei TFT, используемой с дисплеем 3.5 TFT с Arduino
Я надеялся найти что-то вроде растрового шрифта 6x6, чтобы определение таблицы растровых шрифтов изменилось с const unsigned char font_table_16_col[96][16] = {
на const unsigned char font_table_16_col[96][6] = {
, что освободило бы значительный объем памяти. И эксперименты с сокращением таблицы путем удаления строчных букв показали, что это тоже помогло.
Найти альтернативные растровые шрифты оказалось сложнее, чем я думал, представляя себе кого-то с кладезью растровых шрифтов где-то в репозитории GitHub, которого легко найти с помощью поиска или двух.
Я столкнулся с тем, что, хотя я нашел несколько разных примеров растровых шрифтов, не все из них, похоже, совместимы с моим конкретным 3,5-дюймовым ЖК-дисплеем TFT.
Например, вот представления четырех разных растровых шрифтов, показывающие биты растровых изображений для двух символов, восклицательный знак (!) и двойная кавычка (). Кажется, что 5x8 повернут по часовой стрелке на 90 градусов. 8x8 и 16x8 кажутся правильно ориентированными, а 13x8 кажутся перевернутыми.
Генерация приведенного выше растрового представления
Представления растрового шрифта на изображении выше, показывающие различия в ориентации текстовых символов, были сгенерированы простым графическим интерфейсом Windows и отображены с тире (-), представляющим нулевое значение бита, и звездочкой (*), представляющим битовое значение 1. , Это вывод приложения Microsoft Windows с графическим интерфейсом, чей WM_PAINT
обработчик сообщений, рисующий отображаемое изображение, выглядит следующим образом:
int paintFontDisplay(HWND hWnd)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
SetTextAlign(hdc, TA_CENTER);
RECT rect;
GetClientRect(hWnd, &rect);
//Logical units are device dependent pixels, so this will create a handle to a logical font that is 48 pixels in height.
//The width, when set to 0, will cause the font mapper to choose the closest matching value.
//The font face name will be Impact.
HFONT hFont = CreateFont(24, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS,
CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, FIXED_PITCH, TEXT("Courier"));
SelectObject(hdc, hFont);
// TODO: Add any drawing code that uses hdc here...
int iFirst = 0;
int iLast = 10;
POINT outPoint;
outPoint.x = rect.left + 80;
outPoint.y = rect.top + 20;
for (int i = iFirst; i < iLast; i++) {
for (int j = 0; j < 5; j++) {
std::wstring charRep;
for (unsigned char k = 0x80; k; k >>= 1) {
if (font_table_5_col[i][j] & k) {
charRep += '*';
}
else {
charRep += '-';
}
}
TextOut(hdc, outPoint.x, outPoint.y, charRep.c_str(), charRep.length());
outPoint.y += 20;
}
outPoint.y += 20 + 20 * 11;
}
outPoint.x = outPoint.x + 200;
outPoint.y = rect.top + 20;
for (int i = iFirst; i < iLast; i++) {
for (int j = 0; j < 8; j++) {
std::wstring charRep;
for (unsigned char k = 0x80; k; k >>= 1) {
if (font_tabledrawGlyph()
col[i][j] & k) {
charRep += '*';
}
else {
charRep += '-';
}
}
TextOut(hdc, outPoint.x, outPoint.y, charRep.c_str(), charRep.length());
outPoint.y += 20;
}
outPoint.y += 20 + 20 * 8;
}
outPoint.x = outPoint.x + 200;
outPoint.y = rect.top + 20;
for (int i = iFirst; i < iLast; i++) {
for (int j = 0; j < 13; j++) {
std::wstring charRep;
for (unsigned char k = 0x80; k; k >>= 1) {
if (font_table_13_col[i][j] & k) {
charRep += '*';
}
else {
charRep += '-';
}
}
TextOut(hdc, outPoint.x, outPoint.y, charRep.c_str(), charRep.length());
outPoint.y += 20;
}
outPoint.y += 20 + 20 * 3;
}
outPoint.x = outPoint.x + 200;
outPoint.y = rect.top + 20;
for (int i = iFirst; i < iLast; i++) {
for (int j = 0; j < 16; j++) {
std::wstring charRep;
for (unsigned char k = 0x80; k; k >>= 1) {
if (font_table_16_col[i][j] & k) {
charRep += '*';
}
else {
charRep += '-';
}
}
TextOut(hdc, outPoint.x, outPoint.y, charRep.c_str(), charRep.length());
outPoint.y += 20;
}
outPoint.y += 20;
}
EndPaint(hWnd, &ps);
return 0;
}
Первые несколько строк таблиц растровых шрифтов выглядят следующим образом:
// following table from URL https://forum.arduino.cc/t/font-generation-for-bitmaps/161582/11
const unsigned char font_table_5_col[96][5] = {
{ 0x00, 0x00, 0x00, 0x00, 0x00 } // 20
,{ 0x00, 0x00, 0x5f, 0x00, 0x00 } // 21 !
,{ 0x00, 0x07, 0x00, 0x07, 0x00 } // 22 "
,{ 0x14, 0x7f, 0x14, 0x7f, 0x14 } // 23 #
,{ 0x24, 0x2a, 0x7f, 0x2a, 0x12 } // 24 $
// See https://github.com/dhepper/font8x8
const unsigned char font_tabledrawGlyph()
col[96][8] = {
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0020 (space)
{ 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00 }, // U+0021 (!)
{ 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0022 (")
{ 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00 }, // U+0023 (#)
{ 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00 }, // U+0024 ($)
const unsigned char font_table_13_col[96][13] = {
// from URL https://courses.cs.washington.edu/courses/cse457/98a/tech/OpenGL/font.c
// GLubyte rasters[][13] = {
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },/*" ",0*/
{ 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 },/*"!",1*/
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36 },/*""",2*/
{ 0x00, 0x00, 0x00, 0x66, 0x66, 0xff, 0x66, 0x66, 0xff, 0x66, 0x66, 0x00, 0x00 },/*"#",3*/
{ 0x00, 0x00, 0x18, 0x7e, 0xff, 0x1b, 0x1f, 0x7e, 0xf8, 0xd8, 0xff, 0x7e, 0x18 },/*"$",4*/
Преобразование растровых изображений шрифтов для правильного отображения
Я изменил код, отображающий текст с использованием растровых шрифтов, так что для конкретной растровой карты логика отрисовки символов будет выполнять несколько различных видов переводов между представлением растрового шрифта в виде последовательности шестнадцатеричных цифр и тем, как последовательность цифр используется для определить, какие пиксели включить, а какие выключить.
Код для рисования одной строки символа выглядит следующим образом. Схема этой функции заключается в предоставлении контроллеру ЖК-дисплея прямоугольника, определяющего область дисплея, которую нужно изменить, за которой следует серия из двух 8-битных записей для установки двухбайтового значения цвета RGB565 для каждого пикселя в области.
static bool TFTLCD::draw_glyph(unsigned short x0, unsigned short y0, TftColor fg_color, TftColor bg_color, unsigned char bitMap, unsigned char bmWidth, unsigned char flags)
{
// we will fill a single row of 8 pixels by iterating over
// a bitmap font map of which pixels to set to the foreground
// color and which pixels to set to the background color for this
// part of the character to display.
// first determine whether we are scaling the default width by a multiplier
// of 2 or 3 times the default size. this allows us to have different sizes
// of text using the same bitmap font.
if (flags & 0x01)
set_area(x0, y0, x0 + bmWidth * 2 - 1, y0); // scale the default width to double wide
else if (flags & 0x02)
set_area(x0, y0, x0 + bmWidth * 3 - 1, y0); // scale the default width to tripple wide
else
set_area(x0, y0, x0 + bmWidth - 1, y0); // default width and size with no scaling
if (flags & 0x20) { // Font::font_flags & FontTable::Flags_InvertBitOrder
// inverting the order of painting the bits. means the bitmap of the
// font would display the text with each character flipped if we did not do this.
for (unsigned char char_n = 0x80; char_n; char_n >>= 1)
{
if (bitMap & char_n)
{
w_data(fg_color >> 8);
w_data(fg_color);
}
else {
w_data(bg_color >> 8);
w_data(bg_color);
}
if (flags & 0x03) { // double wide or triple wide
if (bitMap & char_n)
{
w_data(fg_color >> 8);
w_data(fg_color);
}
else {
w_data(bg_color >> 8);
w_data(bg_color);
}
if (flags & 0x02) { // triple wide
if (bitMap & char_n)
{
w_data(fg_color >> 8);
w_data(fg_color);
}
else {
w_data(bg_color >> 8);
w_data(bg_color);
}
}
}
}
}
else {
for (unsigned char char_n = 1; char_n; char_n <<= 1)
{
if (bitMap & char_n)
{
w_data(fg_color >> 8);
w_data(fg_color);
}
else {
w_data(bg_color >> 8);
w_data(bg_color);
}
if (flags & 0x03) { // double wide or triple wide
if (bitMap & char_n)
{
w_data(fg_color >> 8);
w_data(fg_color);
}
else {
w_data(bg_color >> 8);
w_data(bg_color);
}
if (flags & 0x02) { // triple wide
if (bitMap & char_n)
{
w_data(fg_color >> 8);
w_data(fg_color);
}
else {
w_data(bg_color >> 8);
w_data(bg_color);
}
}
}
}
}
return 1;
и исходный код, который использует вышеуказанную функцию для рисования полных символов, выглядит следующим образом. Этот код использует функцию drawGlyph()
для рисования серии фрагментов текстового символа сверху вниз. Время выполнения преобразования растрового изображения зависит от представления растрового изображения.
unsigned char glyphFlags = ((Font::font_flags & FontTable::Flags_DoubleWide) ? 1 : 0) | ((Font::font_flags & FontTable::Flags_TripleWide) ? 2 : 0);
if (Font::font_flags & FontTable::Flags_InvertBitOrder) {
glyphFlags |= 0x20;
for (signed char char_m = Font::font_table.nCols - 1; char_m >= 0; char_m--)
{
TFTLCD::draw_glyph(Font::now_x, Font::now_y, Font::font_color, Font::txt_backcolor, Font::font_table.table[char_i_x + char_m], Font::font_table.nCols, glyphFlags);
// shift down to the next row of pixels for the character
Font::now_y++;
if (font_flags & (FontTable::Flags_DoubleHigh | FontTable::Flags_TripleHigh)) {
TFTLCD::draw_glyph(Font::now_x, Font::now_y, Font::font_color, Font::txt_backcolor, Font::font_table.table[char_i_x + char_m], Font::font_table.nCols, glyphFlags);
// shift down to the next row of pixels for the character
Font::now_y++;
}
if (font_flags & FontTable::Flags_TripleHigh) {
TFTLCD::draw_glyph(Font::now_x, Font::now_y, Font::font_color, Font::txt_backcolor, Font::font_table.table[char_i_x + char_m], Font::font_table.nCols, glyphFlags);
// shift down to the next row of pixels for the character
Font::now_y++;
}
}
}
else if (Font::font_flags & FontTable::Flags_RotateBits) {
for (unsigned char char_m = 0; char_m < 8; char_m++)
{
unsigned char rotatedMap = 0;
for (unsigned char char_x = 0; char_x < Font::font_table.nCols; char_x++) {
rotatedMap |= ((Font::font_table.table[char_i_x + char_x] & (1 << char_m)) ? 1 : 0) << char_x;
}
TFTLCD::draw_glyph(Font::now_x, Font::now_y, Font::font_color, Font::txt_backcolor, rotatedMap, 8, glyphFlags);
// shift down to the next row of pixels for the character
Font::now_y++;
if (font_flags & (FontTable::Flags_DoubleHigh | FontTable::Flags_TripleHigh)) {
TFTLCD::draw_glyph(Font::now_x, Font::now_y, Font::font_color, Font::txt_backcolor, rotatedMap, 8, glyphFlags);
// shift down to the next row of pixels for the character
Font::now_y++;
}
if (font_flags & FontTable::Flags_TripleHigh) {
TFTLCD::draw_glyph(Font::now_x, Font::now_y, Font::font_color, Font::txt_backcolor, rotatedMap, 8, glyphFlags);
// shift down to the next row of pixels for the character
Font::now_y++;
}
}
}
else {
for (unsigned char char_m = 0; char_m < Font::font_table.nCols; char_m++)
{
TFTLCD::draw_glyph(Font::now_x, Font::now_y, Font::font_color, Font::txt_backcolor, Font::font_table.table[char_i_x + char_m], Font::font_table.nCols, glyphFlags);
// shift down to the next row of pixels for the character
Font::now_y++;
if (font_flags & (FontTable::Flags_DoubleHigh | FontTable::Flags_TripleHigh)) {
TFTLCD::draw_glyph(Font::now_x, Font::now_y, Font::font_color, Font::txt_backcolor, Font::font_table.table[char_i_x + char_m], Font::font_table.nCols, glyphFlags);
// shift down to the next row of pixels for the character
Font::now_y++;
}
if (font_flags & FontTable::Flags_TripleHigh) {
TFTLCD::draw_glyph(Font::now_x, Font::now_y, Font::font_color, Font::txt_backcolor, Font::font_table.table[char_i_x + char_m], Font::font_table.nCols, glyphFlags);
// shift down to the next row of pixels for the character
Font::now_y++;
}
}
}
Характеристики растрового или растрового шрифта
Существует ряд спецификаций шрифтов, включая растровые шрифты растрового типа. Эти спецификации не обязательно описывают растровые изображения глифов, используемые в таких приложениях, как библиотека KeDei TFT, а скорее предоставляют независимое от устройства описание формата растрового шрифта.
Формат распространения растрового изображения глифа
BITMAP начинает растровое изображение для текущего глифа. За этой линией должна следовать одна линия на пиксель по оси Y. В этом примере глиф имеет высоту 16 пикселей, поэтому за ним следует 16 строк. Каждая строка содержит шестнадцатеричное представление пикселей в строке. 1 бит указывает визуализированный пиксель. Каждая строка округляется до 8-битной (один байт) границы, дополняется нулями справа. В этом примере глиф имеет ширину ровно 8 пикселей и поэтому занимает ровно 8 бит (один байт) на строку, поэтому отступы отсутствуют. Старший бит строки растровых данных представляет крайний левый пиксель.
Руководство разработчика Oracle в Solarix X Window System, глава 4 Поддержка шрифтов по адресу https://docs.oracle.com/cd/E19253-01/816-0279/6m6pd1cvk/index.html содержит таблицу, в которой перечислены несколько различных форматов растровых шрифтов, и говорит следующее:
Как показано в таблице 4–4, многие форматы файлов растровых шрифтов являются двоичными файлами, зависящими от архитектуры. Их нельзя использовать совместно между машинами с разной архитектурой (например, между SPARC и IA).
- Формат распространения растрового изображения, файл .bdf, не двоичный, не зависящий от архитектуры
- Портативный скомпилированный формат, файл .pcf, двоичный файл, не зависящий от архитектуры
- Предустановленный формат Little Endian, двоичный, зависящий от архитектуры
- Готовый формат Big Endian, двоичный, зависящий от архитектуры
PSF (экранный шрифт для ПК), двоичная спецификация, описанная по адресу https://www.win.tue.nl/%7Eaeb/linux/kbd/font-formats-1.html
PSF расшифровывается как экранный шрифт ПК. Формат psf1 без карты Unicode был разработан Х. Питером Анвином примерно в 1989 году для его редактора экранных шрифтов DOS FONTEDIT.EXE. В октябре 1994 года он добавил карту Unicode и программы psfaddtable, psfgettable, psfstriptable для управления ею — см. kbd-0.90. Андрис Брауэр добавил поддержку последовательностей значений Unicode и формата psf2 в сентябре 1999 г. для обработки тибетского языка — см. kbd-1.00.
Microsoft Q65123 из архива ранних статей базы знаний Microsoft https://jeffpar.github.io/kbarchive/kb/065/Q65123/
Форматы файлов шрифтов Microsoft Windows определены как для растровых, так и для векторных шрифтов. Эти форматы могут использоваться интеллектуальными генераторами текста в некоторых модулях поддержки GDI. В частности, векторные форматы чаще используются самим GDI, чем вспомогательными модулями.
Спецификация файла шрифта Metagraphics .fnt https://www.metagraphics.com/metawindow/fonts/fnt-specs.htm
Графическая библиотека Microchip, Шрифты AN1182 в графической библиотеке Microchip (PDF)
Смотрите также
Где я могу найти спецификацию формата .fon?
На этом веб-сайте File Formats есть описания нескольких различных спецификаций шрифтов. https://docs.fileformat.com/font/fnt/
fontedit
, который создает файл .fnt, однако есть шаг экспорта файла .fnt в виде таблицы растровых шрифтов в стиле C. Что вы использовали для генерации исходного кода C/C++, содержащего таблицу растровых шрифтов? - person Richard Chambers   schedule 11.05.2021