Того, кто вышел из народа, обратно уже не заманишь.
народная мудрость

Меню навигации для мобильных

Матричные шрифты вывести на экранчик. Проблема?

Автор Slabovik, 19 Фев., 2024, 17:45

« предыдущая - следующая »

Slabovik

Добрый день.
Интересный момент. Оказывается, процедуру вывода матричных шрифтов на экранчик, чуть ли не каждый раз приходится писать по-новой. Привык - нормально, но поначалу эти битовые перестановку могут показаться кошмаром.
Предлагаю устроить здесь разборки на тему "кто и как выводит".

Вообще, матричный шрифт относится к моноширным шрифтам с фиксированным размером и, как понятно из названия, представляет собой просто битовую матрицу, в которой закодированы пиксели символов, точнее, светятся они, или погашены.

Символ-ДВА.png

Вот так они кодируются. На картинке символ с размером 12x18. 12 точек в ширину, 18 в высоту.
Точки кодируем битами. Значением '1', а погашенные -'0', но это чистая условность - можно и наоборот и даже зашифровать  ;) Но не будем - такая вот форма логически проста и, что немаловажно, визуально проста - можно весь шрифт набрать вручную.
Биты собираются в байты, байты собираются в массив.

Поскольку ширина шрифта-примера больше 8 бит байта, но не дотягивает 16 бит двух байт, каждая строчка всё-равно представлена двумя байтами. Просто 4 бита не используются, а потому в принципе могут содержать абсолютно любую информацию. Но пока не засовывам туда ничего и просто игнорируем.

В самом простом случае запись с данными шрифта представляет собой байтовый массив, типа

const unsigned char MyFont[] { // ... здесь байты символов, идущих перед нашей '2'

0,0,0x1F,0x80,0x30,0xC0,0x60,0x60,0x60,0x60,0x20,0x60,0,0x60,0,0xC0,1,0x80,7,0,0xC,0,0x18,0,0x30,0,0x60,0,0x60,0x60,0x7F,0xE0,0,0,0,0, // это байты нашей '2'
 // ... дальше байты других символов
};

Кстати, кроме этого надо бы определиться с организацией структуры, например, если уж замахиваться на более-менее универсальную процедуру, надо бы ей давать информацию о том, какого размера шрифт она будет выводить, какие символы у нас там есть и т.п.
Щас, подумаем....
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Некоторые шифты можно найти тут:
UTFT Fonts (http://rinkydinkelectronics.com/r_fonts.php)

Программа для создания и редактирования матричных шрифтов:
matrixFont (https://gitlab.com/riva-lab/matrixFont)

Тема на easyelectronics (https://forum.easyelectronics.ru/viewtopic.php?f=35&t=13281&sid=74aee0c483d96c5c102e5900a9f5e650), от которой оттолкнулся.

Ну и count-zero (https://count-zero.ru/2023/st7735/).

Для F0 свои эксперементы сложил сюда (https://github.com/minamonra/my_stm_snippets/tree/main/F0/st7735) (пока не всё допилено).

По поводу структуры, не знаю есть ли какие-то стандарты, но, например шрифты по первой ссылке содержат как раз такую иформацию в первых 4-х байтах.
Программа matrixFont на выходе даёт только массив символов.
Вот сейчас точно не вспомню, но была какая-то программа, которая на выходе давала массив из uint16_t, вот тут не понятно, но мне кажется в таком случае надо раскладывать на два байта.
https://github.com/minamonra/

Slabovik

#2
Оу, MatrixFont ещё живая! С удовольствием стырил себе  8)

Ладно, продолжим.
Получается, каждый набор байт изображения символа, представляется как массив.
Кодов символов обычно 256, часто символы до 20h (32dec) не используются (хотя imho это не совсем правильно).
Если вот такие массивы байт каждого символа располагать один за другим в общем массиве, можно, зная адрес начала всего массива и количество байт, приходящихся на один символ, вычислять адрес начала каждого символа. Это просто.

Но гораздо лучше иметь таблицу адресов символов как массив указателей :: https://metanit.com/c/tutorial/5.6.php
const int *FontSym[] = { &FontSym_x00, &FontSym_x01, &FontSym_x02, ...etc... &FontSym_xFF }; // тут тип int должен полностью вмещать адрес ячейки. Если размер int не достаточен, нужен тип побольше.
Перед этим FontSym_x## должны быть определены как массивы, содержащие коды рисовки конкретных символов.
... ...
const unsigned char FontSym_x2F[] = { ... }; // слэш прямой
const unsigned char FontSym_x30[] = { ... }; // ноль
const unsigned char FontSym_x31[] = { ... }; // единичка
const unsigned char FontSym_x32[] = { 0, 0, 0x1F, 0x80, 0x30, 0xC0, 0x60, 0x60, 0x60, 0x60, 0x20, 0x60, 0, 0x60, 0, 0xC0, 1, 0x80, 7, 0, 0xC, 0, 0x18, 0, 0x30, 0, 0x60, 0, 0x60, 0x60, 0x7F, 0xE0, 0, 0, 0, 0 }; // это наша двоечка из прошлого поста
const unsigned char FontSym_x33[] = { ... }; // тройка
... ...
При такой организации есть сразу несколько фишек.
Первая фишка в том, что сами массивы символов не обязаны идти друг за другом по порядку.
Вторая фишка - символов может быть нарисовано меньше (например, только нужные).
Третья фишка - экономим место за счёт того, что часть символов латиницы и русского выглядят одинаково.
Например, для ASCII CP1251 на месте ссылки на символ xC0 ставим ссылку на x41 (А-A), на месте ссылки на xC2 ставим ссылку на x42 (В-B) и т.д. На местах ссылок отсутствующих (не нарисованных) символов ставим ссылку на какой-нибудь x20 ('пробел') или ещё что-нибудь, чтобы было видно, что "не попал"...

Да, FontSym - это указатель. Его прописываем как константу, чтобы он лёг в ПЗУ.

Как работать.

В функцию вывода нам "прилетает" код символа. Пусть будет это SymCode
используем этот код как индекс для массива : FontSym[SymCode]
Надо иметь указатель в ОЗУ, куда мы достанем адрес матрицы символа с прилетевшим кодом :
Функция вывода символа (SymCode, координаты, цвет, etc...) {
 var int *SymMatrixPtr; // локальная переменная-указатель на матрицу рисуемого символа, про размер int см.выше...
 var unsigned char MatrixCode;
... ...
 SymMatrixPtr = FontSym[SymCode];

По-идее, SymMatrixPtr после этого действия должен содержать адрес самого первого (нулевого) байта массива изображения символа (который на картинке +00 обозначен)
Далее, уже в цикле "разворачивания" битов изображения в байты для посылания на экран, будем читать данные, находящиеся по указателю SymMatrixPtr

MatrixCode = *SymMatrixPtr;

По-идеее, это операция чтения байта кода матрицы символа из памяти ROM в переменную MatrixCode.

После чтения делаем

SymMatrixPtr++; // сдвигаем указатель на следующую ячейку матрицы

Кстати, указателю вообще пофиг на тип данных, на которые он показывает, попадает ли он на те данные, что мы подразумеваем и т.п. Это просто адрес.

зы: просьба меня поправлять, если я где-то ошибся. А ещё очень большая просьба эти функции аккуратно проверять в отладчике. Ибо я и так не силён в Си, дак ещё и по памяти фантазирую без какой-либо проверки...

Ну а следующим этапом пойдёт как раз превращение битов матрицы в байты, посылаемые на экран.
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

#3
Ну вот я позанудствую, зачем нам создавать кучу констант FontSym_xXX, если у нас и так есть массив констант?
Если используем толко цифры, соответственно массив кромсаем.
А если целиком, то очень удобно строки выводить, задав смещение -0x20.


----
Есть ещё Adafruit gfx font generator, формат немного другой на выходе, присутствует второй массив с описанием каждого глифа.
Онлай версия тут https://rop.nl/truetype2gfx/
Приложение прицепил.
https://github.com/minamonra/

Slabovik

#4
Таблица констант-ссылок - Это унификация.
А ещё экономия места за счёт одинаково выглядящих символов с разными кодами. Например, символы от 0 до 31 (dec) можно легко откусить вычитанием, но что делать, когда какие-то из них надо всё-таки нарисовать? Устраивать case? Причём для каждого шрифта своё собственное? Проще сделать таблицу, несуществующие символы направить на символ-пустышку, остальные на реальные изображения.
А экономия за счёт одинаковых. Два одинаковых символа можно нарисовать по одному разу. Вот такой список:

A-А
B-В
C-С
E-Е
P-Р
H-Н
X-Х
O-О
M-М
T-Т
a-а
c-с
e-е
p-р
o-о
x-х
y-у

Это минимум 17 символов. Если рассматривать в рамках шрифта из примера, то 17*36=612 байт долой только за счёт этого.

Далее идут дырки в таблицах. Например, в таблице 1251 их много со 128 кода. Всё выкидывать плохо - там есть нужные символы, и они не подряд. А все подряд в русском языке точно не нужны.

А ещё быстрее. Для получения адреса надо только прочитать из таблицы. Если рассчитывать, то уже понадобится умножать. Впрочем, сейчас операция умножения в процах есть., без неё надо биты поперекладывать, а это долго (привет от старичка 8080 :) )

Согласен, можно сделать по-другому. Входящий код просто сопоставить другому коду через таблицу перекодировки. Это может быть актуально для процессоров с широкой шиной, например таблица перекодировки займёт один на символ, т.е. будет 256 байт. Таблица 16-разрядных ссылок займёт 512 байт. 32-разрядные ссылки, соответственно, килобайт.

Да, давай тогда так и сделаем - будет экономия места. Тогда вся структура будет выглядеть примерно так
const unsigned char SymPosition[255] = { 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, ... // первые 32 символа - это пустышки. Сама "пустышка" у нас нарисована в позиции 0
 1, // символ с индексом 32 (0x20)- пробел, в таблице это второй по счёту (индекс 1)
 2, // символ '!' (0x21) - индекс 2.
// ... и так далее
В этом случае символы не обязательно должны идти по порядку, поэтому легко сделать на входе ASCII 866, 1251, КОИ - только их направить на свою таблицу соответственно коду. Каких-то символов может не быть, причём в произвольном порядке - на их месте просто прописывается код символа-заглушки.

А дальше

  SymMatrixPtr = &FontSym + SymPosition[SymCode]*SymLength;

здесь SymLength - тоже константа, определённая рядом с шрифтом, определяющая, сколько байт занимает один символ. Хорошо было бы, чтобы она со шрифтом лежала в одной структуре. Это для того, чтобы процедуре дать только ссылку на начало структуры шрифта, а процедура сама бы прочитала нужные константы.

Можешь пояснить, что там четыре байта означают в тех шрифтах?

По-идее, шрифт надо сопровождать такими константами:
SymWidth - количество столбцов (ширина)
SymHeight - количество строк (высота)
SymLenght - длина в байтах одного символа. Причём её можно и вычислять, но взять из таблицы просто быстрее.

блин... тут уже впору шрифт заводить как структуру :: https://prog-cpp.ru/c-struct/

и процедуре вывода скармливать указатель на структуру. Оттуда она всё сама прочитает.
Указатель на шрифт (структуру) можно сделать глобальным, типа переключил шрифт - с ним работаем. А можно в качестве входных данных. Тут от конструкции программы больше зависит.

Что получается

struct Font_12x18 {
    const unsigned char Position[255] = { ... ...}; // должно быть обязательно заполнено
    const unsigned char Width = 12; // количество столбцов (ширина) - это пример в ширину 12-точечного
    const unsigned char Height = 18; // количество строк (высота) - наш 18-строчный
    const unsigned char Lenght = 2; длина в байтах одного символа.
// здесь можно вставить один байт для выравнивания. Мало ли...
    const unsigned char Matrix_00[36] = {.. .. .. ...}; символ на позиции 0 'заглушка'
    const unsigned char Matrix_01[36] = {.. .. .. ...}; символ на позиции 1 'пробел'
    ... ...
    const unsigned char Matrix_xx[36] = {.. .. .. ...}; символ на позиции xx
}; // конец структуры
Вот я никак не понял, перед struct нужно ли указать, что она const? Можно ли написать
const struct Font_12x18 .... и не писать внутри у каждого значения const. Попробуй найти...

В общем, получается структура. У ней первые 256 (с 0 по 255) таблица перекодировки. Ещё 4 байта - данные о шрифте. Далее изображения букв.

зы: щас самое неприятное - структура может быть просто массивом.


В процедуре на входе получается {код_символа, позиция, цвета, указатель_на_структуру}

//считаем данные
SymMatrixPtr = указатель_на_структуру + код_символа;
MatrixNum = *SymMatrixPtr; считали номер матрицы, соответствующий коду символа;

SymMatrixPtr = указатель_на_структуру + 256; // Засунули в локальную переменную указатель. Он сейчас показывает на байт с шириной

CharWidth = *SymMatrixPtr; // считали ширину

SymMatrixPtr++;
CharHeight = *SymMatrixPtr; // считали высоту

SymMatrixPtr++;
CharLength = *SymMatrixPtr; // считали длину символа

SymMatrixPtr = указатель_на_структуру + 260 + MatrixNum*CharLength; // вот здесь умножение всплыло

Теперь у нас заполнены локальные переменные и SymMatrixPtr показывает на первый байт матрицы символа.


эх, на деле рассказывать сложнее, чем писать самому. Потому что чуть изменил условия - и всё начинай сначала...
Вообще, по-простому, я бы не структурой делал, а индексным байтовым монолитным массивом. Структуры становятся интересны, когда они переменные.

Если объявлять монолитный массив, то его длина будет получаться (260 + кол-во_символов*длина_символов)
При этом индексы от 0 до 255 - это перекодировка (соответствие),
256 - щирина,
257 - высота,
258 - кол-во байт,
259 - резерв на выравнивание (может нужен будет)
260... матрицы символов.

Тут проще, т.к. можно через индекс доставать данные:

MatrixNum = Font12x18[код_символа];
CharWidth = Font12x18[256];
CharHeight = Font12x18[257];
CharLength = Font12x18[258];
MatrixIndex = 260 + MatrixNum*CharLength; // MatrixIndex содержит индекс первого (нулевого) байта содержащего изображение символа.
Далее достаём байты и превращаем из в поток фон-символ.

А если через указатели - будет также, как выше. Через указатели универсально, т.к. указатель может показывать на любой фонт. Указатель можно с индексом делать, а можно его самого корректировать. По-идее, корректировать указатель код должен быть даже короче, хотя с точки зрения написания выглядит хуже...

Что-то мне подсказывает, что правильно вначале объявить структуру:

struct Font_12x18 { .. описание};

а затем сделать

const Font_12x18 FontName = {и здесь заполнить};


Т.е. struct - это вроде определения типа, а затем этот тип заполняем. Но это не точно... Проверишь?  ;)
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

#5
Так, минутучку, всё это хорошо, можно и нужно свой велосипед изобретать (чем мы частенько и занимаемся).
Но задача, от которой всё пошло - интерфейс под 1,77" TFT дисплей, в часности шрифты, причём такие, чтобы нравились.
Одним из лучших считаю SevenSegment, но размеры готовых, которые нашёл это 32х50 и 32х46 для упомянутого дисплея годятся только в случае отображения одной строкой основного значения, например частоты, или времени, двумя строками уже выглядит не очень.
Понравились два шрифта (оттуда же) Ubuntu и Inconsola, их выводить у меня получилось, а вот строчку из функции печати символа я так и не распарсил.
Конкретно эту:
d1 = CH * 96 + ((b - 1) * 3 + i );
такая она, когда вывожу шрифт Inconsola, для шрифта Ubuntu она такая:
d1 = CH * 96 + ((b - 1) * 3 + i + 4);
Функция:
↓ спойлер ↓
#include "Ubuntu.h"
#include "Inconsola.h"

void print_char_24x32_land(uint8_t CH, uint8_t X, uint8_t Y, uint16_t fcolor, uint16_t bcolor)
{
  uint8_t j, i, b, d;
  uint16_t d1, color;
  CS_DN;
  lcd7735_at(X, Y, X + 31, Y + 23); // Ширина и высота шрифта от 0
  lcd7735_sendCmd(0x2C);
  SPI2SIXTEEN;
  DC_UP;
  for (i = 0; i < 3; i++) { // 3 байта в строке матрици 3*8 = 24 FONTWIDTH
    for (j = 8; j > 0; j--) { 
      for (b = 32; b > 0; b--) { // 32 строки в матрице FONTHEIGHT
        //d1 = CHP + ((b - 1) * 3 + i );
        d1 = (CH * 96 + ((b - 1) * 3 + i + 4);
        d = Ubuntu[d1];
        //d = Inconsola[d1];
        if (d & (1 << (j - 1))) color = fcolor; else color = bcolor;
        while (!(SPI1->SR & SPI_SR_TXE)){};
        SPI1->DR = color;
      }
    }
  }
  while (!(SPI1->SR & SPI_SR_TXE) || (SPI1->SR & SPI_SR_BSY)){};
  CS_UP;
  SPI2EIGHT;
}
[свернуть]
---
Дальше, вчера пробовал вывести шрифт созданный в MatrixFont, причём условие - строки слева-направо, сверху-вниз соблюдено, но опять како-то непопадание.
Вот тестовый вариант этого шрифта
https://github.com/minamonra/my_stm_snippets/blob/main/F0/st7735/st7735v01_sevensegfont/consolas_20_font.h

---
Те, думаю сначала надо понять как выводить уже готовые шрифты, а потом браться за свой, ну или параллельно.
---
ЦитатаМожешь пояснить, что там четыре байта означают в тех шрифтах?
Вот тит объяснение есть:
http://rinkydinkelectronics.com/h_utft_fonts_101.php
Первые два байта размер, вторые начала в таблице ASCII и количество символов, если я правильно понял иностранный. :)
---
Ещё шрифты на подумать:
https://github.com/immortalserg/AdafruitGFXRusFonts/tree/master/FontsRus
---
Ну и наконец варианты с count-zero, там есть компрессия и процедура создания своих шрифтов тоже описана.
---
ЦитатаНо это не точно... Проверишь?
Да, чуть позже, тут то танцы у дочки, то в разъездах... :)
ы. Ещё ссылочка на шрифты, правда только картинки, но для этого тоже видел способ преобразования
https://github.com/RichardBsolut/GT24L24A2Y
https://github.com/minamonra/

Slabovik

Отрицание - Торг - Гнев - Депрессия - Принятие

Мы щас где-то в начальной стадии :)
На деле очень трудно при изучении программирования, когда волшебство значков уже освоено, заставить себя писать более понятно. Но... надо.
На первом этапе пишешь i, j, k ,l...
На втором они всё-таки превращаются в StringCounter, RowCounter, SkipCounter....
И только потом, когда ты окончательно запутываешься, начинаешь писать

var int SkipCounter; // счётчик выведенных вверх за позицию окна строк от начала текста
...

Как-то так ;)

Сейчас задача стоит таким образом:
Придти к осмысленной структуре шрифта. Уже я настаиваю, чтобы у шрифта был более-менее универсальный заголовок, одинаковой структуры для всех и не ограничивающих возможности в рамках всех 256 байт-кодов символов. Только наличие этого свойства уже даст возможность сделать процедуру вывода универсальной - ей будет всё-равно, какие символы есть у выводимого шрифта и какого они размера - это процедура просто возьмёт из заголовка.
Если этого не сделать, к каждому шрифту вынужденно будет тащиться его собственная процедура вывода, работающая только с этим шрифтом. Конечно, когда шрифт один - это пофиг, а когда четыре-пять?

Так что на данный момент задача из двух частей:

а) составить пару-тройку шрифтов по такой структуре в виде .h файлов, скажем, шрифт 8x14, 12x20, 24x40 (по вкусу, но как минимум ширина одного шрифта ровно в байты попадает, одного шрифта - не кратна байтам).

б) добиться от написуемой процедуры вывода считывания CharWidth, CharHeight, CharLength и правильной постановки указателя на первый байт (левый верхний) матрицы символа. Всё это делать в отладчике, ну или в CОМ-порт плевать можно, чтобы в компе на терминале результат смотреть. На производство вывода непосредственно на экран настоятельно рекомендую на данном этапе установить табу, как бы ни хотелось смотреть именно на нём...

Как будут правильно выставляться эти данные для любого символа любого шрифта (с нашим заголовком), перейдём к процедуре превращения байтов в битовый поток и уже его превращение в байты для пикселей экрана.
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Те, что-то типа этого:
↓ спойлер ↓
const unsigned char inconsola24x32bitmaps[] = {
// 16 символов
// В строке 96 байт
0x00,0x10,0x00,0x00,0xFC,0x00,0x01,0xFE,0x00,0x03,0xC7,0x00,0x03,0x83,0x80,0x07,0x01,0x80,0x06,0x01,0xC0,0x06,0x03,0xC0,0x0E,0x07,0xC0,0x0E,0x0E,0xC0,0x0E,0x1C,0xE0,0x0C,0x18,0xE0,0x0C,0x38,0xE0,0x0E,0x70,0xE0,0x0E,0xE0,0xE0,0x0E,0xC0,0xC0,0x0F,0xC0,0xC0,0x07,0x80,0xC0,0x07,0x01,0xC0,0x07,0x01,0x80,0x03,0x83,0x80,0x03,0xC7,0x00,0x00,0xFE,0x00,0x00,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // 0
0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x3E,0x00,0x01,0xFE,0x00,0x00,0xC6,0x00,0x00,0x06,0x00,0x00,0x06,0x00,0x00,0x06,0x00,0x00,0x06,0x00,0x00,0x06,0x00,0x00,0x06,0x00,0x00,0x06,0x00,0x00,0x06,0x00,0x00,0x06,0x00,0x00,0x06,0x00,0x00,0x06,0x00,0x00,0x06,0x00,0x00,0x06,0x00,0x00,0x06,0x00,0x00,0x06,0x00,0x00,0x06,0x00,0x00,0x06,0x00,0x00,0x06,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // 1
0x00,0x10,0x00,0x00,0xFE,0x00,0x01,0xFF,0x00,0x03,0x87,0x80,0x07,0x01,0xC0,0x02,0x01,0xC0,0x00,0x01,0xC0,0x00,0x00,0xC0,0x00,0x01,0xC0,0x00,0x01,0xC0,0x00,0x03,0x80,0x00,0x03,0x80,0x00,0x07,0x00,0x00,0x0E,0x00,0x00,0x1C,0x00,0x00,0x38,0x00,0x00,0x70,0x00,0x00,0xE0,0x00,0x01,0xC0,0x00,0x03,0x80,0x00,0x03,0x80,0x00,0x07,0x00,0x20,0x07,0xFF,0xC0,0x07,0xFF,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // 2
0x00,0x00,0x00,0x00,0xFC,0x00,0x03,0xFF,0x00,0x07,0x07,0x00,0x02,0x03,0x80,0x00,0x01,0x80,0x00,0x01,0x80,0x00,0x01,0x80,0x00,0x03,0x80,0x00,0x07,0x00,0x00,0x1E,0x00,0x00,0x7C,0x00,0x00,0x7F,0x00,0x00,0x07,0x80,0x00,0x03,0x80,0x00,0x01,0xC0,0x00,0x01,0xC0,0x00,0x01,0xC0,0x00,0x01,0xC0,0x02,0x01,0xC0,0x02,0x03,0x80,0x07,0x87,0x80,0x03,0xFF,0x00,0x01,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // 3
0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x0F,0x00,0x00,0x0F,0x00,0x00,0x1F,0x00,0x00,0x3F,0x00,0x00,0x37,0x00,0x00,0x67,0x00,0x00,0x67,0x00,0x00,0xC7,0x00,0x01,0xC7,0x00,0x01,0x87,0x00,0x03,0x07,0x00,0x07,0x07,0x00,0x06,0x07,0x00,0x0F,0xFF,0xE0,0x0F,0xFF,0xE0,0x0F,0xFF,0xE0,0x00,0x07,0x00,0x00,0x07,0x00,0x00,0x07,0x00,0x00,0x07,0x00,0x00,0x07,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // 4
0x00,0x00,0x00,0x03,0xFF,0xC0,0x03,0xFF,0xC0,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x07,0x00,0x00,0x07,0xFE,0x00,0x07,0xFF,0x00,0x07,0x87,0x80,0x07,0x01,0xC0,0x00,0x01,0xC0,0x00,0x00,0xC0,0x00,0x00,0xE0,0x00,0x00,0xE0,0x00,0x00,0xE0,0x00,0x00,0xC0,0x02,0x01,0xC0,0x0E,0x01,0xC0,0x07,0x87,0x80,0x03,0xFF,0x00,0x01,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // 5
0x00,0x08,0x00,0x00,0x7F,0x00,0x00,0xFF,0xC0,0x01,0xC1,0x80,0x03,0x80,0x80,0x03,0x00,0x00,0x07,0x00,0x00,0x07,0x00,0x00,0x06,0x00,0x00,0x06,0x3E,0x00,0x06,0xFF,0x00,0x07,0xC7,0x80,0x07,0x81,0x80,0x07,0x01,0xC0,0x06,0x00,0xC0,0x06,0x00,0xC0,0x06,0x00,0xC0,0x06,0x00,0xC0,0x07,0x00,0xC0,0x03,0x01,0xC0,0x03,0x81,0xC0,0x01,0xC3,0x80,0x00,0xFF,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // 6
0x00,0x00,0x00,0x07,0xFF,0xC0,0x07,0xFF,0xC0,0x00,0x01,0xC0,0x00,0x03,0x80,0x00,0x03,0x80,0x00,0x03,0x00,0x00,0x07,0x00,0x00,0x07,0x00,0x00,0x0E,0x00,0x00,0x0E,0x00,0x00,0x0C,0x00,0x00,0x1C,0x00,0x00,0x1C,0x00,0x00,0x38,0x00,0x00,0x38,0x00,0x00,0x38,0x00,0x00,0x70,0x00,0x00,0x70,0x00,0x00,0x70,0x00,0x00,0xE0,0x00,0x00,0xE0,0x00,0x00,0xE0,0x00,0x01,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // 7
0x00,0x18,0x00,0x00,0xFE,0x00,0x01,0xFF,0x00,0x03,0x83,0x80,0x03,0x01,0x80,0x07,0x01,0xC0,0x07,0x01,0xC0,0x03,0x01,0xC0,0x03,0x83,0x80,0x01,0xC7,0x00,0x00,0xFE,0x00,0x00,0x7C,0x00,0x01,0xFF,0x00,0x03,0xC7,0x80,0x07,0x03,0x80,0x07,0x01,0xC0,0x06,0x00,0xC0,0x0E,0x00,0xE0,0x0E,0x00,0xC0,0x06,0x00,0xC0,0x07,0x01,0xC0,0x07,0x83,0x80,0x03,0xFF,0x80,0x00,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // 8
0x00,0x10,0x00,0x00,0xFE,0x00,0x01,0xFF,0x00,0x03,0x87,0x80,0x07,0x03,0x80,0x07,0x01,0xC0,0x06,0x01,0xC0,0x06,0x00,0xC0,0x06,0x00,0xC0,0x06,0x00,0xC0,0x07,0x00,0xC0,0x07,0x01,0xC0,0x03,0x83,0xC0,0x03,0xFE,0xC0,0x00,0xFC,0xC0,0x00,0x20,0xC0,0x00,0x01,0xC0,0x00,0x01,0xC0,0x00,0x01,0xC0,0x00,0x03,0x80,0x02,0x03,0x80,0x03,0x0F,0x00,0x07,0xFE,0x00,0x01,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // 9
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // <space>
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // :
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x00,0x1E,0x00,0x00,0x1E,0x00,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // .
0x00,0x20,0x00,0x00,0x30,0x00,0x00,0x30,0x00,0x00,0x30,0x00,0x00,0x78,0x00,0x00,0x78,0x00,0x00,0x78,0x00,0x00,0xFC,0x00,0x00,0xCC,0x00,0x00,0xCE,0x00,0x01,0x86,0x00,0x01,0x86,0x00,0x01,0x87,0x00,0x03,0x03,0x00,0x03,0x03,0x80,0x03,0xFF,0x80,0x07,0xFF,0x80,0x06,0x01,0xC0,0x0E,0x01,0xC0,0x0E,0x00,0xC0,0x0C,0x00,0xE0,0x1C,0x00,0xE0,0x1C,0x00,0x70,0x18,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // A
0x00,0x00,0x00,0x1C,0x00,0x70,0x0C,0x00,0x60,0x0E,0x00,0xE0,0x0E,0x00,0xE0,0x06,0x00,0xC0,0x07,0x01,0xC0,0x07,0x01,0xC0,0x03,0x01,0x80,0x03,0x81,0x80,0x03,0x83,0x80,0x01,0x83,0x00,0x01,0xC3,0x00,0x01,0xC7,0x00,0x00,0xC6,0x00,0x00,0xE6,0x00,0x00,0xEE,0x00,0x00,0x6C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x38,0x00,0x00,0x38,0x00,0x00,0x38,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // V
0x00,0x00,0x00,0x18,0x00,0x30,0x18,0x10,0x30,0x1C,0x10,0x30,0x1C,0x18,0x30,0x1C,0x18,0x70,0x0C,0x38,0x60,0x0C,0x3C,0x60,0x0C,0x3C,0x60,0x0C,0x6C,0x60,0x0E,0x6C,0x60,0x0E,0x6E,0x60,0x06,0x66,0xC0,0x06,0xC6,0xC0,0x06,0xC6,0xC0,0x06,0xC7,0xC0,0x07,0x83,0xC0,0x07,0x83,0xC0,0x07,0x83,0x80,0x03,0x83,0x80,0x03,0x01,0x80,0x03,0x01,0x80,0x03,0x01,0x80,0x03,0x01,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // W
};

typedef struct {
  unsigned int offset;
  unsigned char width, height;

} Font;

const Font inconsola24x32numfont[] = {
  { 0, 24, 32},
  {96, 24, 32}
}
[свернуть]
https://github.com/minamonra/

Slabovik

Да, именно.
Только надо засунуть в начало заголовок с таблицей соответствия кодов символов и данные с размерами.
Таблица соответствия решит вопрос о фильтрации - о ней уже не надо будет задумываться.
Данные о размерах дадут возможность их не высчитывать, а просто брать и грузить по мере надобности.

Если не хочется структуру (я сам не могу чётко сделать описание, потому как просто нифига не помню, как правильно), можно просто обойтись массивом. В принципе, проблем не доставит т.к. эта структура по факту константа и нам реально надо, чтобы это был байтовый массив.
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

↓ спойлер ↓
Цитатапотому как просто нифига не помню
А я нифига не знаю, так как учил-то паскаль. :)
[свернуть]
Так, ладно вот напридумывал, шрифт не весь, для упрощения.
Как только теперь этим пользоваться... ?
Особенно указателями.
https://github.com/minamonra/font_test/blob/main/matr_font_sl_01/main.c
Проект в segger, можно скачать и открыть.
https://github.com/minamonra/

Slabovik

Указателями пользоваться просто. В Паскале без них тоже трудно - все динамические данные на указателях работают.

Указатель - это такая же переменная, просто содержащая физический адрес.

var char Sign ; // Sign - переменная. Понятие абстрактное, но удобное.
    int *CharPointer ; // это указатель. Тип int - четыре байта - это как раз размер 32-битного адреса.
    char BytesArray[255] ; // это массив (без const он попадёт в ОЗУ, но для примера это не важно)


Фактически, если указывать CharPointer, то такая штука работает как переменная. Её можно читать, подсчитывать, складывать, делить и т.п.

Чтобы указатель стал иметь смысл, он должен куда-то показывать. Т.е. ему надо присвоить значение.

CharPointer = &Sign ; & - это операция взятия адреса. &Sign означает "адрес Sign".

Таким образом, CharPointer теперь содержит адрес ячейки, в которой лежит значение переменной Sign.

Сейчас интересное.
Взять данные из ячейки (байта), куда показывает указатель

  Temp = *CharPointer ; // в Temp попадёт значение переменной Sign - это потому что CharPointer туда "смотрит"

а если вот так

  Temp = CharPointer ; // в Temp попадёт значение переменной-указателя CharPointer (размерность Temp пока опустим).

И ставим указатель на массив, на его начало

  CharPointer = *BytesArray ; вот так просто.

Если надо на какой-то элемент массива (вдруг там не char, а набор строк неопределённой длины)

  CharPointer = *BytesArray[j] ; указатель смотри на ячейку с элементом под номером j массива BytesArray

Удобство указателей в том, что имея какую-то структуру, её можно двигать по памяти динамически. Она перестаёт быть привязанной к конкретному месту, структур можно заводить много, просто выделяя для них память. Причём ка ОЗУ, так и ПЗУ.

И фишка в том, что структура имеет определённое месторасположение своди данные относительно её начала. Таким образом, зная структуру и её расположение (адрес первого байта), легко достаём из неё данные, где бы они ни находились.

Что касается нашей структуры, уже повторю, что нет острой необходимости её определять как структуру (хотя надо бы).
Мне запись в matr_font_sl_01 пока не видится правильной, ну просто из-за того, что нырок слишком глубокий. Берём помельче (пока побольше работы руками)

Простой массив

const unsigned char Font12x18[] = {  // первые 256 байт - перекодировка

по принципу: входящий код символа является индексом для таблицы, данные по которому соответствуют порядковому номеру (позиции в общем нашем массиве) матрицы изображения этого символа. Тут только 256, без вариантов. Если первые 32 символа отсутствуют - эти указатели должны всё-равно показать на какой-то символ, на пробел, или вон квадрат часто используют, видел поди.

Затем продолжаем заполнять массив.
Надо заполнить данные: Ширина символа, Высота символа, Длина символа в байтах, и пустышка 0 (уже говорил, что не придумал, что сюда засунуть, но 4 байта - это ровно. Пока только поэтому.)

   12, 18, 36, 0, // всё.

/* позиция 0 -- символ "ПРОБЕЛ" */
   и здесь 36 байтов матрицы символа "пробел"

/* позиция 1 -- символ "!" */
   и здесь 36 байтов матрицы символа "!"

и так далее.

Важно! Нужна хотя бы одна матрица символа. Обычно это символ-заглушка, либо пробел.

 Первые 256 байт должны показывать только на существующие позиции. Если символа, соответствующего входящему коду, нет, то он должен показывать на символ-заглушку.

И в конце }

Длина массива - какая получится, т.е. не определяем сразу, но следим за количеством байт в каждом её участке.

 
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

#11
Ну ты меня с массивом окончательно запутал, какой массив делаем?
Про основы указателей я конечно знаю (местами недопонимаю), уж передачу в функции указателя и возвращение значений делал, а вот их использование в структурах пока нет.

:: добавлено 21 Фев., 2024, 16:24
И что-то не понимаю, как тут перемещаться по массиву:
typedef struct {
  unsigned char charcode;
  unsigned int offset;
  unsigned char width, height;
} Glythmap;

const Glythmap inconsola24x32numfont[] = {
  //charcode offset   width  height
  {48,            0,     24,     32}, // 0
  {49,          192,     24,     32}, // 1
  {50,  288, 24, 32}, // 2
  {51,  384, 24, 32}, // 3
  {52,  480, 24, 32}, // 4
  {53,  576, 24, 32}, // 5
  {54,  672, 24, 32}, // 6
  {55,  768, 24, 32}, // 7
  {56,  864, 24, 32}, // 8
  {57,  960, 24, 32}, // 9
  {32, 1056, 24, 32}, // space
  {58, 1152, 24, 32}, // :
  {46, 1248, 24, 32}, // .
  {65, 1344, 24, 32}, // A
  {86, 1440, 24, 32}, // V
};



///
ааа.
Вот так же
Glythmap MyGlyth;
MyGlyth = inconsola24x32numfont[1];
charcode = MyGlyth.charcode;


---
ы.
unsigned int Temp = CharPointer;
incompatible pointer to integer conversion initializing 'unsigned int' with an expression of type 'unsigned int *'; dereference with * [-Wint-conversion]

CharPointer = *BytesArray[1];  //вот так просто.
indirection requires pointer operand ('int' invalid)

---
↓ спойлер ↓
unsigned int Sign = 999;    // Sign - переменная. Понятие абстрактное, но удобное.
unsigned int *CharPointer ; // это указатель. Тип int - четыре байта - это как раз размер 32-битного адреса.
char BytesArray[255] ;      // это массив (без const он попадёт в ОЗУ, но для примера это не важно)
CharPointer = &Sign;
unsigned int Temp = *CharPointer;
// Temp = CharPointer ; // в Temp попадёт значение переменной-указателя CharPointer (размерность Temp пока опустим). 
// И ставим указатель на массив, на его начало
CharPointer = *BytesArray[1];  //вот так просто.
на последней строчке
indirection requires pointer operand ('int' invalid)

[свернуть]
https://github.com/minamonra/

Slabovik

#12
Да, тут поди ошибка

CharPointer = &BytesArray[1];

Потому что взятие адреса - это &

По-моему здесь написано: https://stackoverflow.com/questions/38237355/error-indirection-requires-pointer-operand-int-invalid

гы... я дам точный код только тогда, когда сам его отлажу. А пока могу только пнуть в нужном направлении  ::)

И... я не зря упрощаю и прошу так сделать. Объясню любое колено. Однако, если ты делаешь что-то иначе, я прошу тоже объяснять, что именно ты делаешь и что ожидаешь получить (ну или получается). Потому что откатываться на 0, когда уже есть план, достаточно затратно...

Массив же давай сделаем самый простой, как описал выше. 256 байт - индексы, 4 байта - размеры, далее посимвольные матрицы.
Матрицы не обязательно по порядку кодов, главное, чтобы индексы сопоставления кодов с матрицами были верными.

Если он получится структурой - пусть будет структурой. Но его содержание должно быть идентично и точно в том же порядке..
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Цитата: Slabovik от 21 Фев., 2024, 18:54Массив же давай сделаем самый простой, как описал выше. 256 байт - индексы, 4 байта - размеры, далее посимвольные матрицы.
Матрицы не обязательно по порядку кодов, главное, чтобы индексы сопоставления кодов с матрицами были верными.
Так вот я и не пойму, какой массив, вначале массива 4-ре байта служебные, а остальное одинаковые по размеру битмапы?
Или на каждый битмап свой размер, что мне кажется более удачным вариантом, тогда не все символы могут быть одинаковыми, но тут тогда усложнение будет, начало XY для каждого символа.

---
https://github.com/minamonra/

Slabovik

Тот массив, который надо сделать, про который я рассказывал.
Чужие массивы - это отдельно, сейчас не надо на них смотреть. Можно из них стырить сами матрицы. А структуру массива делаем такую:
Цитата: Slabovik от 21 Фев., 2024, 15:30Простой массив

const unsigned char Font12x18[] = {  // первые 256 байт - перекодировка

по принципу: входящий код символа является индексом для таблицы, данные по которому соответствуют порядковому номеру (позиции в общем нашем массиве) матрицы изображения этого символа. Тут только 256, без вариантов. Если первые 32 символа отсутствуют - эти указатели должны всё-равно показать на какой-то символ, на пробел, или вон квадрат часто используют, видел поди.

Затем продолжаем заполнять массив.
Надо заполнить данные: Ширина символа, Высота символа, Длина символа в байтах, и пустышка 0 (уже говорил, что не придумал, что сюда засунуть, но 4 байта - это ровно. Пока только поэтому.)

  12, 18, 36, 0, // всё.

/* позиция 0 -- символ "ПРОБЕЛ" */
  и здесь 36 байтов матрицы символа "пробел"

/* позиция 1 -- символ "!" */
  и здесь 36 байтов матрицы символа "!"

и так далее.

Важно! Нужна хотя бы одна матрица символа. Обычно это символ-заглушка, либо пробел.

 Первые 256 байт должны показывать только на существующие позиции. Если символа, соответствующего входящему коду, нет, то он должен показывать на символ-заглушку.

И в конце }

Длина массива - какая получится, т.е. не определяем сразу, но следим за количеством байт в каждом её участке.

Свой размер на каждый битмап - это потом, к тому же только ширину можно, потому что высота одинакова для всех битмапов шрифта. Сейчас задача - попасть правильно на битмап и разложить его, независимо от размеров.

Индексы - вот это отсебятина, но потом поймёшь, насколько они жизнь облегчат...
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

А charcode (позиция в таблице ASCII) тогда куда?
ы.
Если же с такой структурой:
const Glythmap inconsola24x32numfont[] = {
  //charcode offset   width  height   length
  {48,            0,     24,     32,      96 }, // 0
  {49,          192,     24,     32,      96 }, // 1
То, при моноширном шрифте три крайние константы будут одинаковы для всех, и по сути тут не нужны.
Не, ну ещё больше меня запутал, давай так, используем мои битмапы 24x32, там их 15 штук, и дальше на них уже накручиваем, добавить другие не проблема.
С индексами не понял.
https://github.com/minamonra/

Slabovik

Charcode, который получает процедура, работает индексом для получения номера позиции матрицы в массиве. Более она не нужна. Потому индексы и занимают первые 256 байт, чтобы не надо было ничего пересчитывать. Они нужны таблицей потому что номер позиции математически не определяется - это просто список.

Такая таблица будет ничем не лучше таблицы указателей, которая не понравилась. Она получится большая. Offset - длинное, минимум 16 бит. length - тоже минимум 16 бит. Лишнее.

Пока а) моноширный шрифт б) индексированный массив.

С индексированный массивом можно и через любимое i работать, код слегка больше, а так то же самое.

Новое пока что - только индексная таблица.
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

#17
Так и где эта индексная таблица? В основном массиве? А сопоставление индекса и битмапа (если битмапов меньше чем 255)?
ы. Сделай уже массив на Си, хотя бы часть, а то жуть какая-то весь день друг-друга понять не можем...  :)  :)  :)  ;D

:: добавлено 21 Фев., 2024, 19:52
А, есть идея, щас.
https://github.com/minamonra/

Slabovik

Гм. Создалось впечатление, что я ничего не писал и мы друг друга абсолютно не понимаем...  :-\

Индексов 256. Потому что Charcode на входе может быть любой из диапазона 0~255

Идём в массив. Первые 0..255 его ячеек - указатели на матрицы.

берём MatrixNumber = FontArray[CharCode];

У нас есть номер матрицы, связывающий CharCode c её реальным положением в массиве.

MatrixNumber всегда показывает на матрицу, которая есть.

Разные CharCode могут показывать через MatrixNumber на одну и ту же матрицу.
Это для того, чтобы одинаково выглядящим символам не рисовать индивидуальные матрицы, а также направлять CharCode, матрицы для которых не определены, на матрицу, используемую в качестве "заглушки".

Это освобождает от необходимости математических вычислений при определении "рисуем этот символ или нет" потому что в общем случае эта математике не определена и для разных шрифтов могут быть определены разные наборы символов.

Это что касается массива. Можно работать так. Но если перейти на указатели, появится возможность эти массивы (т.е. шрифты) менять как перчатки без переписывания процедуры вывода.

Далее, имея MatrixNumber, считаем на основе MatrixLength (т.е. длины в байтах матрицы одного символа) индекс (ну или указатель) на первый байт этой матрицы.
  указатель = matrixNumber * MatrixLength + 260 ; // где 260 - это постоянное смещение из-за индексов и записанных параметров шрифта.

Задача сейчас - прописать контрольный FontArray и добиться правильной постановки указателей соответственно входящему CharCode и имеющихся матриц.
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

#19
Говорю же, есть идея. :)
https://github.com/minamonra/font_test/blob/main/matr_font_sl_01/myfont.h
Похоже на правду? Там только offset использовать можно пока.
--
Главное надо было сказать, что индексы это второй массив, плнятно что можно без структур, но структуры можно использовать для расширения.
В один массив наверное не надо, его тогда весь надо делать unsigned int.
А вот размером в 255 его делать не обязательно, можно же начать с 32 (пробел).
https://github.com/minamonra/

Slabovik

Glythmap - код, оффсет.

На деле нехорошо т.к. для сопоставления оффсета коду придётся прогонять записи с начала таблицы, проверяя на соответствие.

В самом начале я так и предлагал, только для экономии ресурса не просто оффсет, а сразу адрес матрицы. Но это килобайт для 256 символов.

Немного скукожившись, килобайт меняем на один байт, но добавляем одно умножение и сложение для получения адреса. Умножение... долго, где его нет, но у Меги и STM32 они есть, проблемы не должно быть.

Тут ещё проблема в том, что вот эта таблица оффсетов и сами матрицы символов должны быть единой структурой. Ну чтобы оффсеты-то работали.

Что касается 32... ну глянь ради прикола, там символы есть. Нужные, кстати. Например ← (27) или ♀ (11) + ♂ (12)  = ▲▼ )
Поставив -32 полностью обрежешь возможность их использовать. Поставив в индексах "заглушку" - возможность сохранишь, хотя в каких-нибудь шрифтах хватит и 16 символов, а в каких-то 224 реально мало... И математически  это определить - где какие - невозможно...
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Зачем прогонять, там попорядку всё.
Загоняем в Charcode 32 в
offset = inconsola24x32index[Charcode].offset;
на выходе offset пробела.
Я просто не поубирал оттуда коды символов, они там не нужны по стути, но структуру бы оставить для расширения.
https://github.com/minamonra/

Slabovik

Ок, надо "пояснить по переменным" в том смысле, чтобы путаницы не было, и завтра процедурку осилим :)
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

#23
Завтра день нагруженный, но вечером подключусь.  ;)

:: добавлено 21 Фев., 2024, 21:47
На счёт символов до 32-го и других, в конкретном случае, для дисплея 1.77" не вижу в них вообще никакого смысла, даже скобки бы убрал, тк занимают "лишнее" пространство, а некоторые символы можно ручками попиксельно поправить.
Вот пример, в MatrixFont преобразовал шрифт Consolas размером 22pt, на выходе получились матрицы 18x31, скрин ноля:
0-18x31.jpg
Куча пустого места сверху и снизу, если поубирать ненужные, то можно сильно сократить матрицу.

:: добавлено 22 Фев., 2024, 01:39
ыы. Для того же вольтметра достаточно массива из 10-15 символов.
Расположить в порядке цифр, а 10 сделать пробелом, 11 точкой, ну и пару символов V A...
Тогда вывод можно и без индекса делать.

:: добавлено 22 Фев., 2024, 01:45
ыыы. Сконвертированный Consolas шрифт как есть тут.
https://github.com/minamonra/font_test/tree/main/matr_font_sl_01
Ну и myfont.h для экспериментов, только цифры и индексный массив.
https://github.com/minamonra/

Slabovik

Бодаться на тему "а мне это не надо" можно до бесконечности. В вольтметре вообще не стоит проблема шрифтов...

В итоге процедура вывода символа на входе должна иметь:
 - код символа
 - указатель на структуру шрифта (на начало её таблицы индексов/смещений)
 - позиция на экране, куда должен быть выведен символ.
 
Первое, что делает процедура - читает размер шрифта и организует (даёт команду в дисплей) окно для вывода символа

Далее, для вывода любого символа нужны следующие данные:

var unsigned char SymbolWidth - ширина символа в пикселях
var unsigned char MatrixLength - длина матрицы в байтах
var int *MatrixPointer - указатель на первый байт матрицы

Как будет получаться MatrixPointer - дело третье, через номер или через смещение. У каждого способа есть недостатки и преимущества.
Преимущество номера - краткость. 256 байт на всю таблицу.
Недостаток - требуется целочисленное умножение для расчёта смещения.

Преимущество смещения - быстрота. Кроме чтения и сложения никаких действий не требуется. (при этом если указывать не смещение, а абсолютный адрес - int для этого достаточен - и сложения не нужно. Однако это было отвергнуто в самом начале разговора)
Недостатков два. Первый - под таблицу требуется килобайт места. В 4 раза больше, чем для номеров. Второй - его нужно рассчитывать и легко ошибиться. (для ассемблера расчёт, наоборот, прост т.к. делается автоматом по меткам. Для Си я даже не знаю, как такое провернуть).

Резюме: если в процессоре есть операция умножения, по совокупности предпочтительнее номера. Особенно, когда размер шрифта небольшой обидно тратить много места на индексы. Если же операции умножения нет, для сохранения скорости обработки смещения имеют преимущество.

Если шрифт один или пара, без необходимости иметь полный диапазон символов (вольтметры всякие, индикаторы погоды и т.п.), то в этом случае речь об универсальности не идёт и буквы этого шрифта каждый располагает как хочет и выводит как хочет.

/* потребуются ещё вот эти локальные переменные */

var unsigned char MatrixByte; // временная для обработки байта матрицы
var unsigned char BitMask; // маска, накладываемая на байт для получения значения одного бита
var unsigned char BitWidth; // счётчик выведенных по горизонтали точек

/* начало алгоритма вывода. Вывод без поворота
   x матрицы = x символа (ширина)
   y матрицы = y символа (высота)
*/

BitWidth = SymbolWidth ; // начальная установка счётчика

do { MatrixByte = *MatrixPointer ; // чтение очередного байта матрицы
     MatrixPointer = MatrixPointer + 1; // после чтения передвинем указатель на следующий байт матрицы
     MatrixLength = MatrixLength - 1 ; // можно писать просто MatrixLength--; но мне так не нравится...
     BitMask = 0b10000000 ; // предустановка маски и ширины символа на начало для каждого прочитанного байта

     do { if (MatrixByte & BitMask)>0
               { вывод в поток точки цвета символа };
          else { вывод в поток точки цвета фона };

          BitMask = BitMask >> 1 ; // как только бит выедет вправо, BitMask станет равен 0. Значит, вывели все 8 битов в поток
          BitWidth = BitWidth - 1 ; // как только значение станет равным 0. Значит, все биты строки вывели в поток

        } while (BitWidth >0) & (BitMask >0); // если хоть что-то стало 0, надо читать следующий байт - выходим из этого цикла
   
     if BitWidth = 0 { BitWidth = SymbolWidth }; // если выход был по окончании вывода строки - снова предустановим счётчик
                                                 // один раз на символ предустановка лишняя, но организовывать проверку
                                                 // окажется много дороже, чем один лишний раз присвоить значение
   } while MatrixLength >0 ;

Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.